Here is my very simple program that I am trying to debug with cgdb. Problem is once I get to the "scanf" line, it prompts for an input, but once I press enter after entering the input (2 in the example below) it seems to enter into an infinite loop. It works fine in gdb though.
#include <cstdio>
using namespace std;
int main()
{
int n;
scanf("%d", &n);
printf("%d\n", n);
return 0;
}
Here is the execution trace in terminal:
Type "apropos word" to search for commands related to "word"...
Reading symbols from test...done.
(gdb) start
Temporary breakpoint 1 at 0x400585: file test.cpp, line 7.
Starting program: /home/Alex/Desktop/test
Temporary breakpoint 1, main () at test.cpp:7
(gdb) next
2 (this is my input)
Infinite loop starts here.
According to the info page of cgdb, you need to either:
start the program on one terminal, and attach to it with CGDB from another terminal
or pass arguments using the tty window
To invoke the tty window, hit 'T' in command mode (escape)
Extracted from the info page:
Sending I/O to the program being debugged
This technique is similar to getting in and out of "GDB mode". The tty window is not
visible by default. This is because it is only needed if the user
wishes to send data to the program being debugged. To display the tty
window, hit `T' while in command mode.
Related
Question
I have a test program called scan (see code section) that I run in one terminal. The program starts running and asks the user to enter a letter:
$ ./scan
Enter a letter
In another terminal window, I run gdb and attach to the scan process:
$ sudo gdb -p `pidof scan`
(gdb)
I would like to send contents of a binary file called payload as input to the scan process. How can I accomplish this in gdb while the process is attached? Is this possible?
Background
I want to attach to the scan process because I want to examine the program's ASLR address space while running. If I run the process directly with gdb ($ gdb scan), gdb turns off ASLR and I get consistent addressing.
For example, running scan directly with gdb (please note that ... is truncated output):
$ gdb scan
(gdb) info functions
...
0x00000000000005a8 _init
0x00000000000005d0 __stack_chk_fail#plt
0x00000000000005e0 printf#plt
0x00000000000005f0 __isoc99_scanf#plt
...
The addresses shown above are the same every time I run gdb this way, which indicates that gdb turns off ASLR. However, when attaching to a process, the addresses always change. On one run:
$ sudo gdb -p `pidof scan`
(gdb) info functions
...
0x00005598706305a8 _init
0x00005598706305d0 __stack_chk_fail#plt
0x00005598706305e0 printf#plt
0x00005598706305f0 __isoc99_scanf#plt
On another run:
0x000055813ccf65a8 _init
0x000055813ccf65d0 __stack_chk_fail#plt
0x000055813ccf65e0 printf#plt
0x000055813ccf65f0 __isoc99_scanf#plt
The file payload contains a binary payload. I know how to send file contents as input when running gdb normally (e.g. (gdb) run < payload), but not when running with an attached process.
I do not want to copy/paste these file contents into the terminal that is runningscan. I also do not want to turn off ASLR.
Things I have tried/read
I have read the gdb manual and gdb help commands:
(gdb) help
(gdb) help target
(gdb) help attach
(gdb) help obscure
Other StackOverflow questions do not ask about sending input to an attached process:
Pass File Input and Stdin to gdb
How to debug a program that takes user input from stdin with GDB?
How to passing input data in GDB mode for programming C. Already passed parameters and run program
Code
My entire scan program is this:
#include <stdio.h>
int main(int argc, char **argv)
{
char letter[1];
char buffer[8];
printf("Enter a letter: ");
int result = scanf("%s", letter);
printf("You entered: %s\n", letter);
}
I'm following a 'Build your own Lisp' tutorial to teach myself C, and came across some strange behavior related to fgets. Here is a link to the Chapter I am working on: http://www.buildyourownlisp.com/chapter4_interactive_prompt#an_interactive_prompt
The code is a simple base for what will eventually be the REPL. It just prints some info, and then starts a loop that gets and prints user input.
#include <stdio.h>
static char input[2048];
int main(int argc, char** argv) {
puts("Brenlisp Version 0.0.0.0.1");
puts("Press Ctrl+c to Exit\n");
while (1) {
fputs("brenlisp> ", stdout);
fgets(input, 2048, stdin);
fputs(input, stdout);
}
return 0;
}
When I ran the executable from a Bash terminal (Windows 10), the program started but did not print anything to console, nor did it accept/print user input.
bschw#DESKTOP-92VUB1F MINGW64 ~/Projects/brenlisp
$ ./prompt.exe
However, when I ran the executable from CMD, the prompt performed as expected:
C:\Users\bschw\Projects\brenlisp>prompt.exe
BrenLisp Version 0.0.0.0.1
Press Ctrl+c to Exit
brenlisp> works fine
works fine
brenlisp> works fine
brenlisp> ^C
C:\Users\bschw\Projects\brenlisp>
Another curious thing is that when I run the program on WSL, it doesn't print the "^C" string to the console before exiting:
bschw#DESKTOP-92VUB1F:/mnt/c/Users/bschw/Projects/brenlisp$ ./prompt.exe
BrenLisp Version 0.0.0.0.1
Press Ctrl+c to Exit
brenlisp> works
works
brenlisp> works
brenlisp> bschw#DESKTOP-92VUB1F:/mnt/c/Users/bschw/Projects/brenlisp$
Why are these programs behaving differently depending on which shell they're being run from? How could I get the program to work properly on Bash?
In many implementations, the C Standard Library buffers output in internal buffers (*).
Often, for text streams, it does "line buffering" (buffers get emptied after dealing with a '\n'). This appears to be your case. Your output des not contain a newline.
To force the buffers to be emptied, use fflush() for output operations.
printf("not a line");
fflush(stdout); // force buffer emptying
printf("complete line\n"); // line-buffering poses no issue here
(*) input streams may also be buffered, but the management of buffering for input is quite different
Say with this simple code:
#include<stdio.h>
int main(int argc, char** argv){
printf("Hello World!\n");
return 0;
}
After stepping printf("Hello World!\n”); perhaps there’s a command to print that “Hellow World!\n” has been written to STDOUT.
And after return 0 perhaps there’s a command to see the exit codes generated and it will show 0.
Are there such commands or similar in lldb?
LLDB prints the exit status when a process exits:
(lldb) run
Process 76186 launched: '/tmp/a.out' (x86_64)
Process 76186 exited with status = 10 (0x0000000a)
and you can also access it with the SB API's:
(lldb) script lldb.process.GetExitStatus()
10
lldb doesn't have any special knowledge about all the ways a program might read or write data to a pipe, file handle, pty, etc... It also doesn't know how to interpose on file handles and tee-off the output. There's no particular reason it couldn't, but nobody has added that to date.
So you would have to build this yourself. If you know the API your code is using to read and write, you could use breakpoints to observe that - though that might get slow if you are observing a program that reads and writes a lot.
I use a debugging script that runs several related processes in succession with the debugger. I'm currently using -x to execute several commands automatically (such as run). How can I make gdb quit automatically when the debugged process successfully terminates? Adding a quit command to the command file will cause that command to be handled not just on successful termination, but when errors occur also (when I'd rather take over at that point).
Here's an extract of what's going on:
+ gdb -return-child-result -x gdbbatch --args ./mkfs.cpfs /dev/loop0
GNU gdb (GDB) 7.1-ubuntu
Reading symbols from /home/matt/cpfs/mkfs.cpfs...done.
Program exited normally.
Breakpoint 2 at 0x805224f: file log.c, line 32.
(gdb)
Contents of gdbbatch:
start
b cpfs_log if level >= WARNING
I think I have found a complete solution to your question in connection to looking for something similar in How to make gdb send an external notification on receiving a signal?. None of the other guys here seem to have mentioned or discovered gdb hooks.
Based on Matthew's tip about $_exitcode, this is now my app/.gdbinit that achieves exactly the behavior wanted; normal quit on successful termination and drop to gdb prompt, send email, whatnot on everything else:
set $_exitcode = -999
set height 0
handle SIGTERM nostop print pass
handle SIGPIPE nostop
define hook-stop
if $_exitcode != -999
quit
else
shell echo | mail -s "NOTICE: app has stopped on unhandled signal" root
end
end
echo .gdbinit: running app\n
run
gdb sets $_exitcode when the program successfully terminates. You can make use of that - set it to an unlikely value at the start of your script, and only quit at the end if it has changed:
set $_exitcode = -999
# ...
run
# ...
if $_exitcode != -999
quit
end
(Setting $_exitcode to an unlikely value is a bit ugly, but it will otherwise not be defined at all if the program doesn't terminate, and there doesn't seem to be any obvious way of asking "is this variable defined?" in a conditional.)
GDB has a different "language" for interacting with automated programs called GDB/MI (detailed here), but unfortunately, it doesn't look like it supports conditionals, and is expected to run from other programs with parsing and branching. So, it looks like Expect is the easiest (or at least a working) solution:
$ cat gdbrunner
#!/usr/bin/expect -f
#spawn gdb -return-child-result --args ./mkfs.cpfs /dev/loop0
spawn gdb -return-child-result --args [lindex $argv 0]
#send "start\n"
#send "b cpfs_log if level >= WARNING"
send "run\n"
expect {
normally\. { send "quit\n" }
"exited with code" { interact -nobuffer }
}
I tested this with the simple programs:
$ cat prog1.c
int main(void) { return 0; }
$ cat prog2.c
int main(void) { return 1; }
With the following results:
$ ./gdbrunner ./prog1
spawn gdb -return-child-result --args ./prog1
run
(gdb) run
Starting program: /home/foo/prog1
Program exited normally.
(gdb) quit
$ ./gdbrunner ./prog2
spawn gdb -return-child-result --args ./prog2
run
(gdb) run
Starting program: /home/foo/prog2
Program exited with code 01.
(gdb)
Essentially, you have to parse the output and branch using something else. This would of course work with any other program capable of handling input/output of another process, but the above expect script should get you started, if you don't mind Tcl. It should be a little better, and expect the first (gdb) prompt, but works due to stdin buffering.
You can also modify it to use that GDB/MI interface with the -i command-line argument to GDB; its commands and output are a bit more readily parsable, if you will expand to need more advanced features, as you can see in the previously linked documentation.
This question already has answers here:
How can I see the output of an OS X program being run via the Time Profiler in Instruments?
(2 answers)
Closed 3 months ago.
I'm using Xcode on OSX to develop command line C applications. I would also like to use Instruments to profile and find memory leaks.
However, I couldn't find a way to display the console when launching the application from within Instruments. I'm also unable to attach to a running command line process (it exits with an error):
Here's an example code:
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <setjmp.h>
static sigjmp_buf jmpbuf;
void handler(int sig) {
char c[BUFSIZ];
printf ("Got signal %d\n", sig);
printf ("Deseja sair? (s/n) ");
fgets(c, sizeof(c), stdin);
if(c[0] == 's') {
exit(0);
} else {
siglongjmp(jmpbuf, 1);
}
}
int main(void) {
char buf[BUFSIZ];
signal(SIGINT, handler);
sigsetjmp(jmpbuf, 1);
while(1) {
printf(">>>");
fgets(buf, sizeof(buf), stdin);
printf ("Introduziu: %s\n", buf);
}
return(0);
}
Here's the error I got after launching Instruments, and trying to attach to the running process in xcode:
[Switching to process 1475]
[Switching to process 1475]
Error while running hook_stop:
sharedlibrary apply-load-rules all
Error while running hook_stop:
Invalid type combination in ordering comparison.
Error while running hook_stop:
Invalid type combination in ordering comparison.
Error while running hook_stop:
Error while running hook_stop:
Error while running hook_stop:
Error while running hook_stop:
Error while running hook_stop:
Error while running hook_stop:
Error while running hook_stop:
Unable to disassemble __CFInitialize.
Any thoughts?
It's easy. See the screenshot.
It's a little late to contribute to this old thread, however I have found the best way of profiling a command line utility is to use iprofiler (manpage). This allows data to be collected from the command line simply by adding this to the start of the command line:
iprofiler -leaks -d $HOME/tmp
(I have a private temporary directory at $HOME/tmp, so you might need to use /tmp or leave the -d command line option off altogether).
My test scripts automatically add that to the command line if $FINDLEAKS is defined (and will prepend valgrind if running under Linux).
This then generates a .dtps file (actually a directory) which can be loaded and anaylysed using Instruments.
If you are compiling using clang then simply add both -O3 and -g (clang doesn't support the -pg command line option).
You can change the output in the Options dropdown when choosing your target. The output will appear in the system Console (Applications/Utilities/Console).