From 0f4d7435745250d5842805edd8a73bada9137c59 Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Thu, 26 Jul 2012 16:15:39 +0200 Subject: [PATCH 1/1] cvs -z3 -d:pserver:anonymous@libmigdb.cvs.sourceforge.net:/cvsroot/libmigdb co -P libmigdb 2010-05-19T21:30 --- .cvsignore | 3 + ChangeLog | 438 ++++ DJGPP.why | 101 + GPL-license | 340 +++ Makefile | 15 + README | 124 ++ change.log | 224 ++ compress.sh | 4 + doc/html.frt | 282 +++ doc/reference.html | 5072 +++++++++++++++++++++++++++++++++++++++++++++ examples/.cvsignore | 15 + examples/Makefile | 31 + examples/cmds.txt | 3 + examples/linux_test.c | 195 ++ examples/pty_test.c | 141 ++ examples/remote_test.c | 179 ++ examples/target_frames.cc | 23 + examples/test_target.cc | 22 + examples/ticepic.c | 200 ++ examples/x11_cpp_test.cc | 166 ++ examples/x11_fr_test.c | 329 +++ examples/x11_test.c | 192 ++ examples/x11_wp_test.c | 292 +++ files | 16 + src/.cvsignore | 3 + src/Makefile | 55 + src/alloc.c | 308 +++ src/breakpoint.c | 265 +++ src/connect.c | 887 ++++++++ src/cpp_int.cc | 1123 ++++++++++ src/data_man.c | 241 +++ src/error.c | 38 + src/get_free_pty.c | 132 ++ src/get_free_vt.c | 153 ++ src/mi_gdb.h | 972 +++++++++ src/misc.c | 118 ++ src/parse.c | 1923 +++++++++++++++++ src/prg_control.c | 454 ++++ src/stack_man.c | 222 ++ src/symbol_query.c | 32 + src/target_man.c | 119 ++ src/thread.c | 89 + src/var_obj.c | 369 ++++ version.c | 7 + 44 files changed, 15917 insertions(+) create mode 100644 .cvsignore create mode 100644 ChangeLog create mode 100644 DJGPP.why create mode 100644 GPL-license create mode 100644 Makefile create mode 100644 README create mode 100644 change.log create mode 100755 compress.sh create mode 100644 doc/html.frt create mode 100644 doc/reference.html create mode 100644 examples/.cvsignore create mode 100644 examples/Makefile create mode 100644 examples/cmds.txt create mode 100644 examples/linux_test.c create mode 100644 examples/pty_test.c create mode 100644 examples/remote_test.c create mode 100644 examples/target_frames.cc create mode 100644 examples/test_target.cc create mode 100644 examples/ticepic.c create mode 100644 examples/x11_cpp_test.cc create mode 100644 examples/x11_fr_test.c create mode 100644 examples/x11_test.c create mode 100644 examples/x11_wp_test.c create mode 100644 files create mode 100644 src/.cvsignore create mode 100644 src/Makefile create mode 100644 src/alloc.c create mode 100644 src/breakpoint.c create mode 100644 src/connect.c create mode 100644 src/cpp_int.cc create mode 100644 src/data_man.c create mode 100644 src/error.c create mode 100644 src/get_free_pty.c create mode 100644 src/get_free_vt.c create mode 100644 src/mi_gdb.h create mode 100644 src/misc.c create mode 100644 src/parse.c create mode 100644 src/prg_control.c create mode 100644 src/stack_man.c create mode 100644 src/symbol_query.c create mode 100644 src/target_man.c create mode 100644 src/thread.c create mode 100644 src/var_obj.c create mode 100644 version.c diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..a36664c --- /dev/null +++ b/.cvsignore @@ -0,0 +1,3 @@ +*.dst +.*.dst +version diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e2fe890 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,438 @@ +2010-05-19 21:30 set + + * change.log: + + * Modified: [connect.c] To ignore \r characters from gdb. Markus Huebenthal + saw them using gdb 6.8. + * Modified: [prg_control.c] -readnow vs file name order. Don't remmember + who pointed-out it. + +2010-05-19 21:29 set + + * src/prg_control.c: + + * Modified: -readnow vs file name order. Don't remmember who pointed-out + it. + +2010-05-19 21:29 set + + * src/connect.c: + + * Modified: To ignore \r characters from gdb. Markus Huebenthal saw them + using gdb 6.8. + +2010-02-18 14:24 set + + * files: + + * Added: cmds.txt example. + +2010-02-18 14:18 set + + * .cvsignore, change.log, examples/.cvsignore, examples/Makefile, + examples/cmds.txt, examples/ticepic.c, examples/x11_cpp_test.cc, + src/connect.c, src/mi_gdb.h, src/target_man.c: + + * Added: [ticepic.c examples/Makefile] Example of how to use something + different than gdb. + * Added: [x11_cpp_test.cc cmds.txt] Example of how to specify an starting + commands file. + * Added: [connect.c mi_gdb.h] Functionality to indicate a file containing + commands to be executed at start-up and after connection. + * Modified: [mi_gdb.h] Version to 0.8.12. + * Fixed: [target_man.c] Return value for gmi_target_select. + * Added: [.cvsignore] version executable exclusion. + * Added: [examples/.cvsignore] ticepic binary exclusion. + +2007-06-13 14:26 set + + * change.log, compress.sh, files, version.c, src/alloc.c, + src/mi_gdb.h, src/parse.c: + + * Added: "from" field to the mi_frames structure. Suggested by Val Greene + (vgreene/SF). + +2007-05-11 16:16 set + + * change.log, src/cpp_int.cc, src/mi_gdb.h, src/parse.c, + src/target_man.c: + + * Added: Implementation of the -target-download command, used to download + a binary to the remote target. + * Added: An option to MIDebugger::SelectTargetRemote used to load the + binary to the remote target. Useful for embedded systems, tested with + avr-gdb. + +2007-05-10 18:49 set + + * change.log, src/cpp_int.cc, src/mi_gdb.h, src/misc.c: + + * Added: AVR architecture. + * Added: Workaround for bugs in -gdb-show architecture. Seen on gdb 6.4.x. + +2007-05-03 13:51 set + + * change.log, src/cpp_int.cc, src/mi_gdb.h: + + * Added: pic14 architecture. That's for an in-circuit emulator that I'm + developing. + +2007-04-30 18:43 set + + * change.log, src/parse.c: + + * Fixed: When parsing an address from gdb strtol isn't suitable, strtoul + is better. + +2005-09-26 20:38 set + + * change.log, src/parse.c: + + * Fixed: Apple seem to have changed the output type for stack frames to be + a tuple of tuples rather than a list of tuples. Patch from Greg. + +2005-06-03 15:04 set + + * doc/reference.html: + + * Updated: Version to 0.8.10. + +2005-06-03 15:03 set + + * change.log, compress.sh, doc/html.frt, src/mi_gdb.h, src/parse.c: + + * Fixed: Breakpoint parser filled the "type" field instead of the "disp" + field. Patch by Max Kovalenko (madmax/botik/ru). + * Modified: Version to 0.8.10. + +2005-05-13 21:29 set + + * change.log, examples/pty_test.c: + + * Fixed: pty_test.c to also exit when read returns 0 (Linux case). Patch + from Greg. + +2005-05-12 22:23 set + + * change.log, doc/html.frt, doc/reference.html: + + * Modified: Regenerated the docs to include the new documentation about + ptys. Also changed the docs to name version 0.8.9. + +2005-05-12 22:18 set + + * change.log, compress.sh, examples/.cvsignore, examples/Makefile, + examples/pty_test.c, src/Makefile, src/get_free_pty.c, + src/get_free_vt.c, src/mi_gdb.h: + + * Applied patches from Greg Watson (gwatson/lanl/gov): + * Removes apple code from src/get_free_vt.c + * Adds a new file src/get_free_pty.c This is a new functionality to + communicate with the debuggie using a pseudo terminal. + * Adds new mi_pty structure to src/mi_gdb.h + * Modifies src/Makefile to build get_free_pty.o + * Adds new file examples/pty_test.c + * Modifies examples/Makefile to build pty_test + +2005-04-29 22:11 set + + * change.log, src/get_free_vt.c, src/mi_gdb.h, src/parse.c, + src/prg_control.c: + + * Applied patches from Greg Watson (gwatson/lanl/gov): + * Add pty support for Darwin, including extending the mi_aux_term + structure to include a file descriptor for the master side of the pty. + This allows the code to prevent a race condition between checking for a + free pty and opening it. + * Rename mi_get_sttoped() to mi_get_stopped() and expose the interface + in mi_gdb.h. + * Added new gmi_exec_next_cnt() and gmi_exec_stop_cnt() commands. + * Add support for the broken Darwin version of gdb (tuples can contain + values as well as results). + * Fix the 'signal-name' and 'signal-meaning' variable names (were + 'signal_name' and 'signal_meaning'). + * Allow a 'count' argument to be supplied to mi_exec_step() and + mi_exec_next(). + +2004-12-06 16:51 set + + * change.log, compress.sh, src/connect.c, src/mi_gdb.h: + + * Fixed: Compilations problems for SuSE 5.2. Reported by J.B. Lethbridge. + +2004-10-19 18:21 set + + * change.log, src/cpp_int.cc: + + * Fixed: FinishFun member must set the state to running if the command + succeed. + +2004-10-18 21:39 set + + * change.log, compress.sh, src/mi_gdb.h: + + * Modified: Version to 0.8.7. + +2004-10-18 21:35 set + + * change.log, examples/x11_cpp_test.cc, examples/x11_fr_test.c, + examples/x11_wp_test.c, src/connect.c, src/mi_gdb.h: + + * Fixed: Various examples to compile with current code. + * Fixed: Some missing headers. Needed to compile on BSD systems. + +2004-10-16 01:50 set + + * change.log, src/cpp_int.cc: + + * Added: [Cygwin] Some minimal support. Now when the target is selected we + tell gdb to use a new window. It seems to work but the new window have some + serious problems. + +2004-10-07 23:09 set + + * Makefile: + + * Fixed: [Makefile] .PHONY is needed to avoid problems. + +2004-09-17 01:18 set + + * change.log, src/connect.c, src/get_free_vt.c: + + * Modified: To compile with Cygwin. It doesn't mean it works, just + compiles. + +2004-09-13 22:39 set + + * src/data_man.c: + + * Added: Comment about which CVS version of gdb fixes the mi/1770 bug. + +2004-09-10 21:10 set + + * Makefile, change.log, compress.sh, files, src/Makefile, + src/mi_gdb.h: + + * Added: A top level makefile, is just a wrapper for the src/Makefile. + * Modified: Version to 0.8.6. + +2004-09-08 20:09 set + + * change.log, src/cpp_int.cc, src/mi_gdb.h: + + * Added: C++ member to find and cache the architecture. Currently supported + architectures are IA32 (x86) and SPARC. They are the only archs I have at + hand. + +2004-09-07 21:56 set + + * change.log, src/connect.c, src/error.c, src/mi_gdb.h: + + * Fixed: Lack of error report when we failed to create the temporal files + to spawn an X terminal. + * Fixed: Now we test if the X terminal and gdb binary are there and report + proper errors if they are missing. + +2004-09-06 19:13 set + + * change.log, src/data_man.c, src/mi_gdb.h, src/parse.c: + + * Added: Functions to workaround another bug in gdb. I reported it and it + was named "mi/1770". Affects gdb 6.x. + +2004-08-28 06:15 set + + * src/connect.c: + + * Fixed: Oops! + +2004-08-25 22:49 set + + * change.log, src/connect.c, src/cpp_int.cc, src/mi_gdb.h, + src/prg_control.c: + + * Modified: mi_error reset to MI_OK when connecting. + * Modified: MIDebugger::TargetUnselect() to detach only if remote/pid is + stopped. + * Added: execute until address is reached (we had file:line only). + +2004-08-24 23:10 set + + * change.log, src/cpp_int.cc: + + * Modified: PID targets starts stopped. + * Fixed: When debugging an attached process if gdb dies we failed to go to + disconnected. + +2004-08-24 01:45 set + + * change.log, compress.sh, src/connect.c, src/data_man.c, + src/mi_gdb.h, src/prg_control.c, src/var_obj.c: + + * Added: Mechanism to disable psym workarounds. The mechanism allows to + enable/disable workarounds in a simple and compatible way. + * Added: Mechanism to force MI version. + * Added: Enabled a couple of MI v2 things when the version is forced to 2+. + * Added: BreakAfter(mi_bkpt *b) C++ wrapper. + +2004-08-17 22:40 set + + * change.log, compress.sh, src/alloc.c, src/cpp_int.cc, + src/data_man.c, src/mi_gdb.h, src/parse.c: + + * Added: Disassembler functionality. + +2004-08-13 22:36 set + + * change.log, src/connect.c, src/cpp_int.cc, src/error.c, + src/mi_gdb.h, src/parse.c: + + * Modified: The mechanism to set gdb and xterm binaries. Now you can get + the actual default (it searches in PATH) and set a new value that's copied. + * Added: Functions to set the "main" function. + * Added: Time out callback to catch time-outs in gdb response. + * Added: Mechanism to detect gdb died and go to disconnected state. We + already found an operation that makes gdb die. + +2004-08-12 23:21 set + + * change.log, src/mi_gdb.h, src/parse.c, src/thread.c: + + * Added: Command to get info about the threads. It uses a partially + implemented gdb functionality. + +2004-08-09 14:58 set + + * change.log, compress.sh, doc/reference.html: + + * Updated: version and reference to release 0.8.3. + +2004-08-09 01:07 set + + * change.log, doc/reference.html, src/connect.c, src/cpp_int.cc, + src/mi_gdb.h: + + * Added: A function to know the endian of the target. + * Added: A workaround to another bug in gdb: responses like the endian and + architecture are sent to the console and not to the result record. So now + the -gdb-show command traps the console (a copy) and if the RR have no info + and we got something in the console that's returned. + +2004-08-06 22:59 set + + * change.log, src/connect.c, src/cpp_int.cc, src/data_man.c, + src/mi_gdb.h, src/parse.c: + + * Fixed: The gdb dialog was amazingly slow. It was just the fact that I + trusted in the non-blocking flag and sleep, but this annoys the scheduler + and the solution is to just use select, which is the Right Thing To Do (TM) + ;-) + * Modified: MIDebugger::EvalExpression now takes a const char * and cleans + the error number before executing. + * Added: Some very restricted function to read memory content. + +2004-08-04 22:07 set + + * change.log, compress.sh, src/alloc.c, src/connect.c, + src/cpp_int.cc, src/mi_gdb.h, src/parse.c, src/var_obj.c: + + * Modified: The mi_gvar structure to be able to hold a tree of variables. + * Fixed: mi_error_from_gdb not released at exit. + * Fixed: leak in Kill() + * Fixed/Added: Various details of the GNU vars code. + +2004-08-01 02:16 set + + * change.log, doc/reference.html, src/alloc.c, src/breakpoint.c, + src/cpp_int.cc, src/mi_gdb.h, src/parse.c: + + * Added: More fields to the mi_wp structure, useful for the frontend. + * Fixed: Watchpoint expressions must be enclosed using "" to avoid + problems. + * Fixed: wp_mode name to mi_wp_mode. + * Added: int MIDebugger::WatchDelete(mi_wp *w). + * Fixed: sr_wp_scope doesn't report a full wp, just the number. + +2004-07-30 23:17 set + + * change.log, src/connect.c, src/cpp_int.cc: + + * Added: More workarounds to gdb bugs. I fille the cli/1725, gdb/1726 and + gdb/1727 bug reports. I hope they get fixed :-( + +2004-07-28 23:09 set + + * change.log, src/alloc.c, src/breakpoint.c, src/cpp_int.cc, + src/mi_gdb.h, src/parse.c: + + * Fixed: Some mess in the bkpts structure. The "times" field is how many + times we hit a breakpoint, just information. The real value is "ignore". + * Fixed: Missing parse of the "cond" field for breakpoints. + * Added: A C++ function to set a breakpoint from a mi_bkpt structure. It + supports the four modes to specify a location. + +2004-07-26 00:58 set + + * change.log, src/alloc.c, src/mi_gdb.h: + + * Added: More fields to the mi_bkpt structure. + +2004-07-21 21:43 set + + * change.log, src/prg_control.c, doc/html.frt, doc/reference.html, + examples/target_frames.cc: + + * Added: A workaround to a bug in gdb: the two stages load of symtabs + (using partial ones) is not reliable (fails to find the symtab in some + cases). So now we force the load with "readnow". + * Fixed: Lack of string.h in examples/target_frames.cc. Reported by Thiago. + +2004-07-21 01:42 set + + * change.log, compress.sh, files: + + * Fixed: directory in compressed sources. + +2004-07-20 22:38 set + + * .cvsignore, change.log, compress.sh, files, examples/.cvsignore, + src/.cvsignore, src/cpp_int.cc, src/mi_gdb.h, src/target_man.c: + + * Added: C++ wrapper to attach to a running process. Also modified the low + level function to return the current frame. + * Modified: Now if we get an error while the state is running we assume the + previous "running" was wrong and we are in fact stopped. Until now I saw + two cases where gdb says we are running and then shows an error. + +2004-07-20 15:31 set + + * DJGPP.why, GPL-license, README, compress.sh, files, doc/html.frt, + doc/reference.html, examples/Makefile, examples/linux_test.c, + examples/remote_test.c, examples/target_frames.cc, + examples/test_target.cc, examples/x11_cpp_test.cc, + examples/x11_fr_test.c, examples/x11_test.c, + examples/x11_wp_test.c, src/Makefile, src/alloc.c, + src/breakpoint.c, src/connect.c, src/cpp_int.cc, src/data_man.c, + src/error.c, src/get_free_vt.c, src/mi_gdb.h, src/misc.c, + src/parse.c, src/prg_control.c, src/stack_man.c, + src/symbol_query.c, src/target_man.c, src/thread.c, + src/var_obj.c: + + Imported sources, current version is 0.8.0 + +2004-07-20 15:31 set + + * DJGPP.why, GPL-license, README, compress.sh, files, doc/html.frt, + doc/reference.html, examples/Makefile, examples/linux_test.c, + examples/remote_test.c, examples/target_frames.cc, + examples/test_target.cc, examples/x11_cpp_test.cc, + examples/x11_fr_test.c, examples/x11_test.c, + examples/x11_wp_test.c, src/Makefile, src/alloc.c, + src/breakpoint.c, src/connect.c, src/cpp_int.cc, src/data_man.c, + src/error.c, src/get_free_vt.c, src/mi_gdb.h, src/misc.c, + src/parse.c, src/prg_control.c, src/stack_man.c, + src/symbol_query.c, src/target_man.c, src/thread.c, + src/var_obj.c: + + Initial revision + diff --git a/DJGPP.why b/DJGPP.why new file mode 100644 index 0000000..4f3cbda --- /dev/null +++ b/DJGPP.why @@ -0,0 +1,101 @@ +DJGPP Problems and why it isn't currently supported: + + Traditionaly djgpp never used gdb frontends. The reason is not so obvious. +The main problem is that DOS isn't a multitasking OS. So you can't open a +pipe to a child a process and multitask. The pipe command exists but you are +blocked until the child finishes. +For this reason there is no gdb frontend for djgpp. Instead djgpp users use +full debuggers with a frontend included. That's the case of RHIDE (in this +particular case gdb is inside RHIDE). + But most people using djgpp uses a multitasking OS, at least for development. +The most common setup is Windows. Additionally you could use two DOS boxes +and get real-hardware-multitask ;-) + The two possible setups are: + +1) Windows (win32): The frontend communicates with gdb using some IPC +mechanism. The frontend uses the "start" (or similar) command to call gdb and +then sends/receives the commands/responses using the IPC mechanism. +Here Windows does the multitasking. +That's completly transparent in most cases because the frontend starts gdb +indicating which file to debug and the same front end controls gdb (including +the end of sesssion). + +2) Two DOS boxes: The user must run gdb in one of the boxes indicating to +connect to the other using some networking protocol. On the other side the +frontend is started and waits for a incoming connection. +This is a little bit more complicated because: +a) The user must setup a network. +b) The user must start things manually. + + But there are some problems that doesn't allow it: + +GDB side: + + GDB remote debugging isn't like this. It was designed for remote debug +using multitasking systems and not things like DOS. When you use the remote +debugging you run "gdbserver" on the "target" end and "gdb" on the local side +(host). But then you are again in the same situation, you can't control gdb +using pipes or similar mechanisms. + + The solution for this is to tell gdb "Hey! use IPC for your command loop". +But the problem is that gdb is a mess. It have a very nice abstraction for +I/O implemented like classes (see ui-file.c). So you have gdb_stdout and +gdb_stdin structures. But not all the code uses it, in fact the "readline" +part (gdb prompt and input) bypass it. + + To solve this you have to redirect at low level. Something like: + +a) Open a socket. +b) dup2 stdin/stdout + + This is the easy part because you have to avoid this redirection in the +child or the output of the debuggy will also go throu the IPC channel. But I +think it isn't that hard because gdb switches the stdout/in attributes when +running the child process, so I think it is possible to also dup2 to the old +handles while the child is running. + + In any case all of this means changes in gdb. + + + +DJGPP side: + + The "open socket and dup2" above mentioned mechanism works for POSIX systems +like Linux (I tested it and worked, I was able to control gdb using TCP/IP +sockets). But djgpp isn't POSIX. + + DJGPP have a nice mechanism to for "File System Extensions", but when I +tried it I found that dup2 doesn't work for extensions. I'm not sure if it +can be done with some hack or if the last version of djgpp version solves +this problem. + + It most probably means that djgpp have to be modified. May be I'm wrong. + + + +IPC what? + + Inter-Process Communication is quite simple on POSIX systems but for djgpp +... Well, you can use a networking protocol, but again isn't that simple. + For TCP/IP you have: + +a) libsocket, it looks like it doesn't work very well with VSOCK 2. I tried +it on Windows 98 SE and it failed. +b) Watt-32, it needs a special NDIS driver for Windows. + + Another option could be using mslot library. It seems to work (at least for +Windows). It uses the "Mail Slot" mechanism. One problem I found in this +library is that it implements the "read" FSEXT in a "non-blocking" way and +doesn't honor the O_NONBLOCKING flag. The changes to fix it are really +simple. According to mslot docs they are available for plain DOS. + + +Conclusion: + + It looks like is possible to achieve the above mentioned setups. But changes +have to be made to gdb, djgpp and existing IPC libraries. + The gdb part seems to be the most complex and harder to be introduced in +main gdb. + + +SET diff --git a/GPL-license b/GPL-license new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/GPL-license @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f873b16 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +#!/usr/bin/make + +.PHONY: libmigdb + +all: libmigdb + +libmigdb: + $(MAKE) -C src + +clean: + $(MAKE) -C src clean + +install: + $(MAKE) -C src install + diff --git a/README b/README new file mode 100644 index 0000000..0f61c6f --- /dev/null +++ b/README @@ -0,0 +1,124 @@ +Introduction: +------------ + +This library is an attempt to support the GDB/MI interface. MI stands for +machine interface. In this mode gdb sends responses that are "machine +readable" instead of "human readable" + +Objetive: +-------- + +To implement a C and C++ interface to talk with gdb using it. + +Advantages: +---------- + +* The responses should be i18n independent and with less problems to be parsed. +* New versions shouldn't break the parser. + +Disadvantages: +------------- + +* The responses are quite complex. +* The responses can't be easily read by a human. + +Motivation: +---------- + +To add remote debugging to SETEdit. RHIDE lacks it. + +Currently supported platcforms: +------------------------------ + +Currently we support: + +* Linux console +* Linux X11 + +The code should be usable by other systems that provides POSIX API and where +gdb is available. + +How to compile and install: +-------------------------- + +Change to the "src" directory and run "make". Change to root and run "make +install". +The "Makefile" could need changes. The PREFIX is /usr you most probably will +want to change it to /usr/local. + +How to run examples: +------------------- + +Switch to the "examples" directory and run "make". Read the comments at the +beggining of the examples. +They show how to use the library for different targets. + +* linux_test: Linux console. Shows how to set breakpoints and watchpoints. +* remote_test: Remote debugging using TCP/IP connection. Shows breakpoints. +* x11_cpp_test: Shows how to use the C++ wrapper. The examples is for X11. +* x11_fr_test: Linux X11. Shows how to use "frames" and "variable objects". +* x11_test: Linux X11. Shows how to set breakpoints and watchpoints. +* x11_wp_test: Linux X11. Shows how to set watchpoints. + +Function reference and help: +--------------------------- + +An incomplete reference can be found in the "doc" directory. I suggest +looking at the examples with the reference at hand. + + +Author: +------ + +Salvador E. Tropea +Email: user: set, server: users.sf.net + + Telephone: (+5411) 4759-0013 + Postal Address: + Salvador E. Tropea + Curapaligue 2124 + (1678) Caseros - 3 de Febrero + Prov: Buenos Aires + Argentina + +----------------------------------------------------------------------------- + +Random notes: +------------ + +Debug strategies: + +1) Remote using TCP/IP: This methode is quite limited. One amazing thing I +noticed is that you can't set watchpoints. Other important differences are +that the remote target starts "running", it means you have to use continue +instead of run and it also means that command line arguments must be provided +at the remote end. +The way that's implemented you need a full gdb on the local end. That's ok +when you want to release the remote end from some of the heavy tasks (like +loading all the debug info), but doesn't help if what you want is just +control gdb from a remote machine sending the commands throu TCP/IP. The last +could allow debugging DOS applications under Windows exploting the Windows +multitasking. + +2) Local under X: We start an xterm child that runs: + +<--- /tmp/xxxxxx.sh +tty > /tmp/yyyyyy +sleep 100d +<--- + +That's "xterm -e /bin/sh /tmp/xxxxxx.sh &" +Then we read /tmp/yyyyyy file to know the ttyname, delete both files and +tell gdb to use this terminal. +This is implemented by: gmi_start_xterm() + +3) Local for Linux console: We can open a new terminal (as X and Allegro +does with tty8 and higher). +This is implemented by: gmi_look_for_free_vt() + +4) Local for Linux console, same terminal: We tell gdb to use the current +terminal, but before sending an async command we so Suspend and when we get +an async response "stopped" we Resume. This is less functional and more +complex. + + diff --git a/change.log b/change.log new file mode 100644 index 0000000..e490f31 --- /dev/null +++ b/change.log @@ -0,0 +1,224 @@ +$Log: change.log,v $ +Revision 1.40 2010/05/19 19:30:23 set +* Modified: [connect.c] To ignore \r characters from gdb. Markus Huebenthal +saw them using gdb 6.8. +* Modified: [prg_control.c] -readnow vs file name order. Don't remmember +who pointed-out it. + +Revision 1.39 2010/02/18 13:18:05 set +* Added: [ticepic.c examples/Makefile] Example of how to use something +different than gdb. +* Added: [x11_cpp_test.cc cmds.txt] Example of how to specify an starting +commands file. +* Added: [connect.c mi_gdb.h] Functionality to indicate a file containing +commands to be executed at start-up and after connection. +* Modified: [mi_gdb.h] Version to 0.8.12. +* Fixed: [target_man.c] Return value for gmi_target_select. +* Added: [.cvsignore] version executable exclusion. +* Added: [examples/.cvsignore] ticepic binary exclusion. + +Revision 1.38 2007/06/13 12:26:47 set +* Added: "from" field to the mi_frames structure. Suggested by Val Greene +(vgreene/SF). +* Modified: Version to 0.8.11. + +Revision 1.37 2007/05/11 14:16:24 set +* Added: Implementation of the -target-download command, used to download +a binary to the remote target. +* Added: An option to MIDebugger::SelectTargetRemote used to load the +binary to the remote target. Useful for embedded systems, tested with +avr-gdb. + +Revision 1.36 2007/05/10 16:49:11 set +* Added: AVR architecture. +* Added: Workaround for bugs in -gdb-show architecture. Seen on gdb 6.4.x. + +Revision 1.35 2007/05/03 11:51:09 set +* Added: pic14 architecture. That's for an in-circuit emulator that I'm +developing. + +Revision 1.34 2007/04/30 16:43:49 set +* Fixed: When parsing an address from gdb strtol isn't suitable, strtoul +is better. + +Revision 1.33 2005/09/26 18:38:35 set +* Fixed: Apple seem to have changed the output type for stack frames to be +a tuple of tuples rather than a list of tuples. Patch from Greg. + +Revision 1.32 2005/06/03 13:03:19 set +* Fixed: Breakpoint parser filled the "type" field instead of the "disp" +field. Patch by Max Kovalenko (madmax/botik/ru). +* Modified: Version to 0.8.10. + +Revision 1.31 2005/05/13 19:29:54 set +* Fixed: pty_test.c to also exit when read returns 0 (Linux case). Patch +from Greg. + +Revision 1.30 2005/05/12 20:23:02 set +* Modified: Regenerated the docs to include the new documentation about +ptys. Also changed the docs to name version 0.8.9. + +Revision 1.29 2005/05/12 20:18:56 set +* Applied patches from Greg Watson (gwatson/lanl/gov): + * Removes apple code from src/get_free_vt.c + * Adds a new file src/get_free_pty.c This is a new functionality to + communicate with the debuggie using a pseudo terminal. + * Adds new mi_pty structure to src/mi_gdb.h + * Modifies src/Makefile to build get_free_pty.o + * Adds new file examples/pty_test.c + * Modifies examples/Makefile to build pty_test + +Revision 1.28 2005/04/29 20:11:13 set +* Applied patches from Greg Watson (gwatson/lanl/gov): + * Add pty support for Darwin, including extending the mi_aux_term + structure to include a file descriptor for the master side of the pty. + This allows the code to prevent a race condition between checking for a + free pty and opening it. + * Rename mi_get_sttoped() to mi_get_stopped() and expose the interface + in mi_gdb.h. + * Added new gmi_exec_next_cnt() and gmi_exec_stop_cnt() commands. + * Add support for the broken Darwin version of gdb (tuples can contain + values as well as results). + * Fix the 'signal-name' and 'signal-meaning' variable names (were + 'signal_name' and 'signal_meaning'). + * Allow a 'count' argument to be supplied to mi_exec_step() and + mi_exec_next(). + +Revision 1.27 2004/12/06 15:51:04 set +* Fixed: Compilations problems for SuSE 5.2. Reported by J.B. Lethbridge. + +Revision 1.26 2004/10/19 16:21:40 set +* Fixed: FinishFun member must set the state to running if the command +succeed. + +Revision 1.25 2004/10/18 19:39:17 set +* Modified: Version to 0.8.7. + +Revision 1.24 2004/10/18 19:35:40 set +* Fixed: Various examples to compile with current code. +* Fixed: Some missing headers. Needed to compile on BSD systems. + +Revision 1.23 2004/10/15 23:50:37 set +* Added: [Cygwin] Some minimal support. Now when the target is selected we +tell gdb to use a new window. It seems to work but the new window have some +serious problems. + +Revision 1.22 2004/09/16 23:18:26 set +* Modified: To compile with Cygwin. It doesn't mean it works, just +compiles. + +Revision 1.21 2004/09/10 19:10:20 set +* Added: A top level makefile, is just a wrapper for the src/Makefile. +* Modified: Version to 0.8.6. + +Revision 1.20 2004/09/08 18:09:46 set +* Added: C++ member to find and cache the architecture. Currently supported +architectures are IA32 (x86) and SPARC. They are the only archs I have at +hand. + +Revision 1.19 2004/09/07 19:56:28 set +* Fixed: Lack of error report when we failed to create the temporal files +to spawn an X terminal. +* Fixed: Now we test if the X terminal and gdb binary are there and report +proper errors if they are missing. + +Revision 1.18 2004/09/06 17:13:11 set +* Added: Functions to workaround another bug in gdb. I reported it and it +was named "mi/1770". Affects gdb 6.x. + +Revision 1.17 2004/08/25 20:49:52 set +* Modified: mi_error reset to MI_OK when connecting. +* Modified: MIDebugger::TargetUnselect() to detach only if remote/pid is +stopped. +* Added: execute until address is reached (we had file:line only). + +Revision 1.16 2004/08/24 21:10:21 set +* Modified: PID targets starts stopped. +* Fixed: When debugging an attached process if gdb dies we failed to go to +disconnected. + +Revision 1.15 2004/08/23 23:45:23 set +* Added: Mechanism to disable psym workarounds. The mechanism allows to +enable/disable workarounds in a simple and compatible way. +* Added: Mechanism to force MI version. +* Added: Enabled a couple of MI v2 things when the version is forced to 2+. +* Added: BreakAfter(mi_bkpt *b) C++ wrapper. + +Revision 1.14 2004/08/17 20:40:11 set +* Added: Disassembler functionality. + +Revision 1.13 2004/08/13 20:36:08 set +* Modified: The mechanism to set gdb and xterm binaries. Now you can get +the actual default (it searches in PATH) and set a new value that's copied. +* Added: Functions to set the "main" function. +* Added: Time out callback to catch time-outs in gdb response. +* Added: Mechanism to detect gdb died and go to disconnected state. We +already found an operation that makes gdb die. + +Revision 1.12 2004/08/12 21:21:43 set +* Added: Command to get info about the threads. It uses a partially +implemented gdb functionality. + +Revision 1.11 2004/08/09 12:58:23 set +* Updated: version and reference to release 0.8.3. + +Revision 1.10 2004/08/08 23:07:00 set +* Added: A function to know the endian of the target. +* Added: A workaround to another bug in gdb: responses like the endian and +architecture are sent to the console and not to the result record. So now +the -gdb-show command traps the console (a copy) and if the RR have no info +and we got something in the console that's returned. + +Revision 1.9 2004/08/06 20:59:44 set +* Fixed: The gdb dialog was amazingly slow. It was just the fact that I +trusted in the non-blocking flag and sleep, but this annoys the scheduler +and the solution is to just use select, which is the Right Thing To Do (TM) +;-) +* Modified: MIDebugger::EvalExpression now takes a const char * and cleans +the error number before executing. +* Added: Some very restricted function to read memory content. + +Revision 1.8 2004/08/04 20:07:31 set +* Modified: The mi_gvar structure to be able to hold a tree of variables. +* Fixed: mi_error_from_gdb not released at exit. +* Fixed: leak in Kill() +* Fixed/Added: Various details of the GNU vars code. + +Revision 1.7 2004/08/01 00:16:13 set +* Added: More fields to the mi_wp structure, useful for the frontend. +* Fixed: Watchpoint expressions must be enclosed using "" to avoid +problems. +* Fixed: wp_mode name to mi_wp_mode. +* Added: int MIDebugger::WatchDelete(mi_wp *w). +* Fixed: sr_wp_scope doesn't report a full wp, just the number. + +Revision 1.6 2004/07/30 21:17:08 set +* Added: More workarounds to gdb bugs. I fille the cli/1725, gdb/1726 and +gdb/1727 bug reports. I hope they get fixed :-( + +Revision 1.5 2004/07/28 21:09:40 set +* Fixed: Some mess in the bkpts structure. The "times" field is how many +times we hit a breakpoint, just information. The real value is "ignore". +* Fixed: Missing parse of the "cond" field for breakpoints. +* Added: A C++ function to set a breakpoint from a mi_bkpt structure. It +supports the four modes to specify a location. + +Revision 1.4 2004/07/25 22:58:07 set +* Added: More fields to the mi_bkpt structure. + +Revision 1.3 2004/07/21 19:43:39 set +* Added: A workaround to a bug in gdb: the two stages load of symtabs +(using partial ones) is not reliable (fails to find the symtab in some +cases). So now we force the load with "readnow". +* Fixed: Lack of string.h in examples/target_frames.cc. Reported by Thiago. + +Revision 1.2 2004/07/20 23:42:56 set +* Fixed: directory in compressed sources. + +Revision 1.1 2004/07/20 20:38:42 set +* Added: C++ wrapper to attach to a running process. Also modified the low +level function to return the current frame. +* Modified: Now if we get an error while the state is running we assume the +previous "running" was wrong and we are in fact stopped. Until now I saw +two cases where gdb says we are running and then shows an error. + diff --git a/compress.sh b/compress.sh new file mode 100755 index 0000000..7903952 --- /dev/null +++ b/compress.sh @@ -0,0 +1,4 @@ +#!/bin/sh +gcc -o version version.c +cd .. +tar jcvf libmigdb-`libmigdb/version`.tar.bz2 `cat libmigdb/files` diff --git a/doc/html.frt b/doc/html.frt new file mode 100644 index 0000000..92082d3 --- /dev/null +++ b/doc/html.frt @@ -0,0 +1,282 @@ +# +# This file sets the behavior of the txh generator +# +# Lines starting with # or spaces are skiped except in strings or in [Generate] +# Be carefull with [ it delimits sections! +# + +[Configuration] +# +# No external program is needed! +# @.html means: Copy the temporal file to xxxxx.html +# +CommandLine=@.html +Name="Direct HTML 3.X format" + +[Delimiters] +# Up to 11 characters +SectionStart=/**[txh]** +# Up to 11 characters +SectionEnd=*********/ + +[Variables] +# +# Up to 16 definitions +# +# Codes for the behavior of the definitions: +# 1 Normal, put the content if found. +# 2 Repeat, use the last value found in the file, ~no is an exeption, ~clear stops +# 3 If not found replace by the prototype. +# 4 If not found replace by the class. +# 5 If not found replace by the name of the function +# 6 It disables the node generation for this comment. The variable is +# stored in the first variable that have associations and is in the comment. +# +# Additionally there are 2 special variables: +# 90 Name of the file +# 91 Line number of the end of the comment +# 92 Name of the var 0 in the format: ~0 ~~Distinguish{(~Distinguish)~} (node name) +# +# 0 The first variable is the main index variable +# +AddDefinition=Function,5 +# 1 +AddDefinition=Class,4 +# 2 +AddDefinition=Include,2 +# 3 +AddDefinition=Module,2 +# 4 +AddDefinition=Prototype,3 +# 5 +AddDefinition=Description,1 +# 6 +AddDefinition=Return,1 +# 7 +AddDefinition=Example,1 +# 8 +AddDefinition=Comments,6 +# 9 +AddDefinition=Command,1 +# +# It says what variable is added to distinguish between 2 vars 0 that are equal +# +Distinguish=1 + +[Associations] +# +# Up to 8 associations +# +# The associations are between the 0 variable and another variable. +# +# Name in main menu, node, variable, optional to add to each node +# +AddAssoc=List by classes,Classes,1,(class) +AddAssoc=List by modules,Modules,3 +AddAssoc=List by files,Files,90 +AddAssoc=List by GDB/MI command,Command,9 + +[Replace] +# +# All must be delimited by ", they can be used in the GenMain section +# +# Title of the HTML +Constant="GDB/MI library" + +# Description for the help +Constant="This document describes the functions of libmigdb library +@p +This document applies to version 0.8.10 of the GDB/Machine interface +library. +@p +Most of the C++ interface is fully documented. But not all the C +interface is docummented. +@p +The name of the high level C functions are derived from the corresponding +GDB/MI command. For this reason I recommend using the chapter 'GDB/MI' of +gdb documentation as main reference. As an example: The gdb command +'-thread-list-ids' is implemented in the gmi_thread_list_ids function. This +function uses the lowlevel function mi_thread_list_ids. +@p +The mi_gdb.h header contains a lot of comments about what does each +function. And the examples show how to them together. +@p +" + +[Commands] +# +# You can enclose these values between " to make more clear where +# they start and end. Use \n,\r,\t and \" like in C. Use \ at the +# end to concatenate like in C. +# +# @p = end of paragraph +# +EndOfPar="

" +# +# @* = break line +# +BreakLine="
" +# +# @{value} is the special cross ref. +# ~0 Is the visible name of a reference +# ~1 Is the real name of a reference +# +CrossRef="~0" +# +# What we must get from a @@ sequence +# +Double@="@" + +[DefinedCommands] +# +# The format is @{parameters ...} +# +subtitle="~0

" +pre=

+/pre=
+link="~1" +mailto="~1" + +# +# This section says how to translate ASCIIs +# +[ASCIIConvert] + =á +‚=é +¡=í +¢=ó +£=ú +¤=ñ +¥=Ñ +­=¡ +¨=¿ +„=ä +‰=ë +‹=ï +”=ö +=ü +š=Ü +…=à +Š=è +=ì +•=ò +—=ù +<=< +>=> + +# +# Use ~number to use one variable +# Use ~~number{} for conditional, all the code inside {} will become +# conditional +# This section isn't passed for the macro expansion so here you are +# free to use special commands for the formater. +# +[GenNode] +


+ +
~0 (~90 ~91)
+ +Syntax

+ +~~2{ +

+# This line is a comment, but the next is code
+ #include <~2>
+
+~} +~~4{ +
+ ~4;
+
+~} +~~1{ +Member of the class: +~1 +

+~} + +~~5{ +Description

+ +~5 +

+~} +~~6{ +Return Value

+ +~6 +

+~} +~~7{ +Example

+ +~7 +

+~} + +[GenMenu] +Start="

" +# +# ~1 is the visible name +# ~2 is the name of the node +# +Entry="
  • ~1" +End="
  • " + +# +# ~1 is the name of the association +# ~2 is the menu for it +# +[GenAssoMain] +


    + +

    ~1

    + +~2 +# +# ~1 is the name of the association +# ~2 is the name without the distinguish +# ~3 is the comment for it +# ~4 is the menu for it +# +[GenAssoRest] +


    + +

    ~2

    + +~3 + +~4 + +# +# ~1 Main menu +# ~2 Name of the function list node +# ~3 Menu for all the functions +# ~4 All the associations code +# ~5 All the function nodes +# ~50+ Values from section Replace +# +[GenMain] + + +~50 + + +

    ~50

    + +~51 + +~1 + +


    + +

    ~2

    + +~3 + +~4 + +~5 + + + diff --git a/doc/reference.html b/doc/reference.html new file mode 100644 index 0000000..453f0ad --- /dev/null +++ b/doc/reference.html @@ -0,0 +1,5072 @@ + + +GDB/MI library + + +

    GDB/MI library

    + +This document describes the functions of libmigdb library + +

    + +This document applies to version 0.8.10 of the GDB/Machine interface +library. + +

    + +Most of the C++ interface is fully documented. But not all the C +interface is docummented. + +

    + +The name of the high level C functions are derived from the corresponding +GDB/MI command. For this reason I recommend using the chapter 'GDB/MI' of +gdb documentation as main reference. As an example: The gdb command +'-thread-list-ids' is implemented in the gmi_thread_list_ids function. This +function uses the lowlevel function mi_thread_list_ids. + +

    + +The mi_gdb.h header contains a lot of comments about what does each +function. And the examples show how to them together. + +

    + + + +

    +
  • Alphabetical list of functions +
  • List by classes +
  • List by modules +
  • List by files +
  • List by GDB/MI command +
  • + + + +


    + +

    Alphabetical List

    + + +
  • BreakDelete (MIDebugger) +
  • Breakpoint (MIDebugger) +
  • Breakpoint (MIDebugger) <1> +
  • BreakpointFull (MIDebugger) +
  • CallStack (MIDebugger) +
  • Connect (MIDebugger) +
  • Continue (MIDebugger) +
  • Disconnect (MIDebugger) +
  • EvalExpression (MIDebugger) +
  • FillTypeVal (MIDebugger) +
  • FinishFun (MIDebugger) +
  • gmi_break_delete +
  • gmi_break_insert +
  • gmi_break_insert_full +
  • gmi_break_insert_full_fl +
  • gmi_break_set_condition +
  • gmi_break_set_times +
  • gmi_break_state +
  • gmi_break_watch +
  • gmi_data_evaluate_expression +
  • gmi_dir +
  • gmi_end_aux_term +
  • gmi_end_pty +
  • gmi_exec_continue +
  • gmi_exec_finish +
  • gmi_exec_interrupt +
  • gmi_exec_kill +
  • gmi_exec_next +
  • gmi_exec_next_cnt +
  • gmi_exec_next_instruction +
  • gmi_exec_return +
  • gmi_exec_run +
  • gmi_exec_step +
  • gmi_exec_step_cnt +
  • gmi_exec_step_instruction +
  • gmi_exec_until +
  • gmi_exec_until_addr +
  • gmi_file_symbol_file +
  • gmi_full_var_create +
  • gmi_gdb_exit +
  • gmi_gdb_set +
  • gmi_gdb_show +
  • gmi_gdb_version +
  • gmi_look_for_free_pty +
  • gmi_look_for_free_vt +
  • gmi_set_exec +
  • gmi_stack_info_depth +
  • gmi_stack_info_depth_get +
  • gmi_stack_info_frame +
  • gmi_stack_list_arguments +
  • gmi_stack_list_arguments_r +
  • gmi_stack_list_frames +
  • gmi_stack_list_frames_r +
  • gmi_stack_list_locals +
  • gmi_stack_select_frame +
  • gmi_start_xterm +
  • gmi_target_attach +
  • gmi_target_detach +
  • gmi_target_select +
  • gmi_target_terminal +
  • gmi_thread_list_all_threads +
  • gmi_thread_list_ids +
  • gmi_thread_select +
  • gmi_var_assign +
  • gmi_var_create +
  • gmi_var_create_nm +
  • gmi_var_delete +
  • gmi_var_evaluate_expression +
  • gmi_var_info_expression +
  • gmi_var_info_num_children +
  • gmi_var_info_type +
  • gmi_var_list_children +
  • gmi_var_set_format +
  • gmi_var_show_attributes +
  • gmi_var_show_format +
  • gmi_var_update +
  • GoTo (MIDebugger) +
  • GoTo (MIDebugger) <1> +
  • Kill (MIDebugger) +
  • mi_connect_local +
  • mi_disconnect +
  • mi_force_version +
  • mi_get_error_str +
  • mi_get_workaround +
  • mi_look_for_free_pty +
  • mi_look_for_free_vt +
  • mi_set_workaround +
  • MIDebugger (MIDebugger) +
  • ModifyExpression (MIDebugger) +
  • Poll (MIDebugger) +
  • ReturnNow (MIDebugger) +
  • Run (MIDebugger) +
  • RunOrContinue (MIDebugger) +
  • RunToMain (MIDebugger) +
  • SelectTargetLinux (MIDebugger) +
  • SelectTargetPID (MIDebugger) +
  • SelectTargetRemote (MIDebugger) +
  • SelectTargetTTY (MIDebugger) +
  • SelectTargetX11 (MIDebugger) +
  • Send (MIDebugger) +
  • StepOver (MIDebugger) +
  • Stop (MIDebugger) +
  • TargetUnselect (MIDebugger) +
  • TraceInto (MIDebugger) +
  • WatchDelete (MIDebugger) +
  • Watchpoint (MIDebugger) +
  • ~MIDebugger (MIDebugger) +
  • + + + +


    + +

    Classes

    + + +
  • MIDebugger +
  • + + +


    + +

    MIDebugger

    + + + + +
  • BreakDelete (MIDebugger) +
  • Breakpoint (MIDebugger) +
  • Breakpoint (MIDebugger) <1> +
  • BreakpointFull (MIDebugger) +
  • CallStack (MIDebugger) +
  • Connect (MIDebugger) +
  • Continue (MIDebugger) +
  • Disconnect (MIDebugger) +
  • EvalExpression (MIDebugger) +
  • FillTypeVal (MIDebugger) +
  • FinishFun (MIDebugger) +
  • GoTo (MIDebugger) +
  • GoTo (MIDebugger) <1> +
  • Kill (MIDebugger) +
  • MIDebugger (MIDebugger) +
  • ModifyExpression (MIDebugger) +
  • Poll (MIDebugger) +
  • ReturnNow (MIDebugger) +
  • Run (MIDebugger) +
  • RunOrContinue (MIDebugger) +
  • RunToMain (MIDebugger) +
  • SelectTargetLinux (MIDebugger) +
  • SelectTargetPID (MIDebugger) +
  • SelectTargetRemote (MIDebugger) +
  • SelectTargetTTY (MIDebugger) +
  • SelectTargetX11 (MIDebugger) +
  • Send (MIDebugger) +
  • StepOver (MIDebugger) +
  • Stop (MIDebugger) +
  • TargetUnselect (MIDebugger) +
  • TraceInto (MIDebugger) +
  • WatchDelete (MIDebugger) +
  • Watchpoint (MIDebugger) +
  • ~MIDebugger (MIDebugger) +
  • + + + +


    + +

    Modules

    + + +
  • Allocator. +
  • Breakpoint table commands. +
  • C++ Interface. +
  • Connect. +
  • Data manipulation. +
  • Error. + Comment: + Translates error numbers into messages. +
  • Linux VT. +
  • Miscellaneous commands. +
  • Parser. +
  • Program control. +
  • pseudo terminal +
  • Stack manipulation. +
  • Symbol query. +
  • Target manipulation. +
  • Thread commands. +
  • Variable objects. +
  • + + +


    + +

    Allocator.

    + +Most alloc/free routines are here. Free routines must accept NULL +pointers. Alloc functions must set mi_error. +

    + + +

    + + + + +


    + +

    Breakpoint table commands.

    + +GDB/MI commands for the "Breakpoint Table Commands" section. +

    + + +

    +gdb command:          Implemented?
    +
    +-break-after          Yes
    +-break-condition      Yes
    +-break-delete         Yes
    +-break-disable        Yes
    +-break-enable         Yes
    +-break-info           N.A. (info break NUMBER) (*)
    +-break-insert         Yes
    +-break-list           No (*)
    +-break-watch          Yes
    +
    + +(*) I think the program should keep track of the breakpoints, so it will +be implemented when I have more time. +

    + + +

    +
  • gmi_break_delete +
  • gmi_break_insert +
  • gmi_break_insert_full +
  • gmi_break_insert_full_fl +
  • gmi_break_set_condition +
  • gmi_break_set_times +
  • gmi_break_state +
  • gmi_break_watch +
  • + + + +


    + +

    C++ Interface.

    + +Implements a very simple (naive ;-) C++ wrapper. +

    + + +

    +
  • BreakDelete (MIDebugger) +
  • Breakpoint (MIDebugger) +
  • Breakpoint (MIDebugger) <1> +
  • BreakpointFull (MIDebugger) +
  • CallStack (MIDebugger) +
  • Connect (MIDebugger) +
  • Continue (MIDebugger) +
  • Disconnect (MIDebugger) +
  • EvalExpression (MIDebugger) +
  • FillTypeVal (MIDebugger) +
  • FinishFun (MIDebugger) +
  • GoTo (MIDebugger) +
  • GoTo (MIDebugger) <1> +
  • Kill (MIDebugger) +
  • MIDebugger (MIDebugger) +
  • ModifyExpression (MIDebugger) +
  • Poll (MIDebugger) +
  • ReturnNow (MIDebugger) +
  • Run (MIDebugger) +
  • RunOrContinue (MIDebugger) +
  • RunToMain (MIDebugger) +
  • SelectTargetLinux (MIDebugger) +
  • SelectTargetPID (MIDebugger) +
  • SelectTargetRemote (MIDebugger) +
  • SelectTargetTTY (MIDebugger) +
  • SelectTargetX11 (MIDebugger) +
  • Send (MIDebugger) +
  • StepOver (MIDebugger) +
  • Stop (MIDebugger) +
  • TargetUnselect (MIDebugger) +
  • TraceInto (MIDebugger) +
  • WatchDelete (MIDebugger) +
  • Watchpoint (MIDebugger) +
  • ~MIDebugger (MIDebugger) +
  • + + + +


    + +

    Connect.

    + +This module handles the dialog with gdb, including starting and stopping +gdb. +

    + + +GDB Bug workaround for "file -readnow": I tried to workaround a bug using +it but looks like this option also have bugs!!!! so I have to use the +command line option --readnow. +It also have a bug!!!! when the binary is changed and gdb must reload it +this option is ignored. So it looks like we have no solution but 3 gdb bugs +in a row. + +

    +
  • gmi_end_aux_term +
  • gmi_start_xterm +
  • mi_connect_local +
  • mi_disconnect +
  • mi_force_version +
  • mi_get_workaround +
  • mi_set_workaround +
  • + + + +


    + +

    Data manipulation.

    + +GDB/MI commands for the "Data manipulation" section. +

    + + +

    +gdb command:                       Implemented?
    +
    +-data-disassemble                  Yes
    +-data-evaluate-expression          Yes
    +-data-list-changed-registers       No
    +-data-list-register-names          Yes
    +-data-list-register-values         No
    +-data-read-memory                  No
    +-display-delete                    N.A. (delete display)
    +-display-disable                   N.A. (disable display)
    +-display-enable                    N.A. (enable display)
    +-display-insert                    N.A. (display)
    +-display-list                      N.A. (info display)
    +-environment-cd                    No
    +-environment-directory             Yes, MI v1 implementation
    +-environment-path                  No
    +
    + +Notes: +

    + + +1) -display* aren't implemented. You can use CLI command display, but the +results are sent to the console. So it looks like the best is to manually +use -data-evaluate-expression to emulate it. +

    + + +2) GDB bug mi/1770: Affects gdb<=6.2, when you ask for the names of the +registers you get it plus the name of the "pseudo-registers", but if you +try to get the value of a pseudo-register you get an error saying the +register number is invalid. I reported to gdb-patches@sources.redhat.com +on 2004/08/25 and as I didn't get any answer I filled a bug report on +2004/09/02. The patch to fix this annoying bug is: + +Index: gdb/mi/mi-main.c +=================================================================== +RCS file: /cvs/src/src/gdb/mi/mi-main.c,v +retrieving revision 1.64 +diff -u -r1.64 mi-main.c +--- gdb/mi/mi-main.c 3 Aug 2004 00:57:27 -0000 1.64 ++++ gdb/mi/mi-main.c 25 Aug 2004 14:12:50 -0000 +@ -423,7 +423,7 @ + case, some entries of REGISTER_NAME will change depending upon + the particular processor being debugged. + +- numregs = NUM_REGS; ++ numregs = NUM_REGS + NUM_PSEUDO_REGS; + + if (argc == 0) + { +---- + +Note I had to remove an end of comment in the patch to include it here. +This bug forced me to create another set of functions. The only way is to +first get the values and then the names. +Fixed by Changelog entry: + +2004-09-12 Salvador E. Tropea <set@users.sf.net> + Andrew Cagney <cagney@gnu.org> + + * mi/mi-main.c (mi_cmd_data_list_changed_registers) + (mi_cmd_data_list_register_values) + (mi_cmd_data_write_register_values): Include the PSEUDO_REGS in + the register number computation. + +

    +
  • gmi_data_evaluate_expression +
  • gmi_dir +
  • + + + +


    + +

    Error. + Comment: + Translates error numbers into messages.

    + + + + +
  • mi_get_error_str +
  • + + + +


    + +

    Linux VT.

    + +Helper to find a free VT. That's 100% Linux specific. +

    + + The code comes from "lconsole.c" from Allegro project and was originally +created by Marek Habersack and then modified by George Foot. I addapted it +to my needs and changed license from giftware to GPL. +

    + + +

    +
  • gmi_look_for_free_vt +
  • mi_look_for_free_vt +
  • + + + +


    + +

    Miscellaneous commands.

    + +GDB/MI commands for the "Miscellaneous Commands" section. +

    + + +

    +gdb command:       Implemented?
    +
    +-gdb-exit          Yes
    +-gdb-set           Yes
    +-gdb-show          Yes
    +-gdb-version       Yes
    +
    + + +
  • gmi_gdb_exit +
  • gmi_gdb_set +
  • gmi_gdb_show +
  • gmi_gdb_version +
  • + + + +


    + +

    Parser.

    + +Parses the output of gdb. It basically converts the text from gdb into a +tree (could be a complex one) that we can easily interpret using C code. + + + + + + +


    + +

    Program control.

    + +GDB/MI commands for the "Program Control" section. +

    + + +

    +gdb command:                   Implemented?
    +
    +-exec-abort                    N.A. (*) (kill, but with non-interactive options)
    +-exec-arguments                Yes
    +-exec-continue                 Yes  ASYNC
    +-exec-finish                   Yes  ASYNC
    +-exec-interrupt                Yes  ASYNC
    +-exec-next                     Yes  ASYNC
    +-exec-next-instruction         Yes  ASYNC
    +-exec-return                   Yes
    +-exec-run                      Yes  ASYNC
    +-exec-show-arguments           N.A. (show args) see gmi_stack_info_frame
    +-exec-step                     Yes  ASYNC
    +-exec-step-instruction         Yes  ASYNC
    +-exec-until                    Yes  ASYNC
    +-file-exec-and-symbols         Yes
    +-file-exec-file                No
    +-file-list-exec-sections       N.A. (info file)
    +-file-list-exec-source-files   N.A.
    +-file-list-shared-libraries    N.A.
    +-file-list-symbol-files        N.A.
    +-file-symbol-file              Yes
    +
    + +(*) gmi_exec_kill implements it, but you should ensure that +gmi_gdb_set("confirm","off") was called. +

    + + +GDB Bug workaround for -file-exec-and-symbols and -file-symbol-file: This +is complex, but a real bug. When you set a breakpoint you never know the +name of the file as it appears in the debug info. So you can be specifying +an absolute file name or a relative file name. The reference point could be +different than the one used in the debug info. To solve all the combinations +gdb does a search trying various combinations. GDB isn't very smart so you +must at least specify the working directory and the directory where the +binary is located to get a good chance (+ user options to solve the rest). +Once you did it gdb can find the file by doing transformations to the +"canonical" filename. This search works OK for already loaded symtabs +(symbol tables), but it have a bug when the search is done for psymtabs +(partial symtabs). The bug is in the use of source_full_path_of (source.c). +This function calls openp indicating try_cwd_first. It makes the search file +if the psymtab file name have at least one dirseparator. It means that +psymtabs for files compiled with relative paths will fail. The search for +symtabs uses symtab_to_filename, it calls open_source_file which finally +calls openp without try_cwd_first.
    +To workaround this bug we must ensure gdb loads *all* the symtabs to memory. +And here comes another problem -file-exec-and-symbols doesn't support it +according to docs. In real life that's a wrapper for "file", but as nobody +can say it won't change we must use the CLI command. + +

    +
  • gmi_exec_continue +
  • gmi_exec_finish +
  • gmi_exec_interrupt +
  • gmi_exec_kill +
  • gmi_exec_next +
  • gmi_exec_next_cnt +
  • gmi_exec_next_instruction +
  • gmi_exec_return +
  • gmi_exec_run +
  • gmi_exec_step +
  • gmi_exec_step_cnt +
  • gmi_exec_step_instruction +
  • gmi_exec_until +
  • gmi_exec_until_addr +
  • gmi_file_symbol_file +
  • gmi_set_exec +
  • gmi_target_terminal +
  • + + + +


    + +

    pseudo terminal

    + +Helper to find a free pseudo terminal. Use this if you need to manage + input *and* output to the target process. If you just need output then + define a handler for target output stream records (assuming that this + is working for your particular version of gdb). + Usage: + + mi_pty *pty = gmi_look_for_free_pty(); + if (pty) gmi_target_terminal(mih, pty->slave); + ... + * reading from pty->master will get stdout from target * + * writing to pty->master will send to target stdin * + + Note: Contributed by Greg Watson (gwatson lanl gov) + + +
  • gmi_end_pty +
  • gmi_look_for_free_pty +
  • mi_look_for_free_pty +
  • + + + +


    + +

    Stack manipulation.

    + +GDB/MI commands for the "Stack Manipulation" section. +

    + + +

    +gdb command:              Implemented?
    +
    +-stack-info-frame         Yes, implemented as "frame"
    +-stack-info-depth         Yes
    +-stack-list-arguments     Yes
    +-stack-list-frames        Yes
    +-stack-list-locals        Yes
    +-stack-select-frame       Yes
    +
    + + +
  • gmi_stack_info_depth +
  • gmi_stack_info_depth_get +
  • gmi_stack_info_frame +
  • gmi_stack_list_arguments +
  • gmi_stack_list_arguments_r +
  • gmi_stack_list_frames +
  • gmi_stack_list_frames_r +
  • gmi_stack_list_locals +
  • gmi_stack_select_frame +
  • + + + +


    + +

    Symbol query.

    + +GDB/MI commands for the "Symbol Query" section. +

    + + +

    +gdb command:              Implemented?
    +-symbol-info-address      N.A. (info address, human readable)
    +-symbol-info-file         N.A.
    +-symbol-info-function     N.A.
    +-symbol-info-line         N.A. (info line, human readable)
    +-symbol-info-symbol       N.A. (info symbol, human readable)
    +-symbol-list-functions    N.A. (info functions, human readable)
    +-symbol-list-types        N.A. (info types, human readable)
    +-symbol-list-variables    N.A. (info variables, human readable)
    +-symbol-list-lines        No (gdb 6.x)
    +-symbol-locate            N.A.
    +-symbol-type              N.A. (ptype, human readable)
    +
    + +Note: +

    + + +Only one is implemented and not in gdb 5.x. +

    + + +

    + + + + +


    + +

    Target manipulation.

    + +GDB/MI commands for the "Target Manipulation" section. +

    + + +

    +-target-attach                  Yes (implemented using attach)
    +-target-compare-sections        N.A. (compare-sections)
    +-target-detach                  Yes
    +-target-download                No
    +-target-exec-status             N.A.
    +-target-list-available-targets  N.A. (help target)
    +-target-list-current-targets    N.A. (info file among other things)
    +-target-list-parameters         N.A.
    +-target-select                  Yes
    +
    + + +
  • gmi_target_attach +
  • gmi_target_detach +
  • gmi_target_select +
  • + + + +


    + +

    Thread commands.

    + +GDB/MI commands for the "Thread Commands" section. +

    + + +

    +gdb command:              Implemented?
    +-thread-info              N.A.
    +-thread-list-all-threads  Yes, implemented as "info threads"
    +-thread-list-ids          Yes
    +-thread-select            Yes
    +
    + + +
  • gmi_thread_list_all_threads +
  • gmi_thread_list_ids +
  • gmi_thread_select +
  • + + + +


    + +

    Variable objects.

    + +GDB/MI commands for the "Variable Objects" section. +

    + + +

    +gdb command:              Imp? Description:
    +-var-create               Yes  create a variable object
    +-var-delete               Yes  delete the variable object and its children
    +-var-set-format           Yes  set the display format of this variable
    +-var-show-format          Yes  show the display format of this variable
    +-var-info-num-children    Yes  tells how many children this object has
    +-var-list-children        Yes* return a list of the object's children
    +-var-info-type            Yes  show the type of this variable object
    +-var-info-expression      Yes  print what this variable object represents
    +-var-show-attributes      Yes  is this variable editable?
    +-var-evaluate-expression  Yes  get the value of this variable
    +-var-assign               Yes  set the value of this variable
    +-var-update               Yes* update the variable and its children
    +
    + +Notes: +

    + +1) I suggest letting gdb to choose the names for the variables.
    +2) -var-list-children supports an optional "show values" argument in MI v2. +It isn't implemented.
    + +

    + + +* MI v1 and v2 result formats supported. +

    + + +

    +
  • gmi_full_var_create +
  • gmi_var_assign +
  • gmi_var_create +
  • gmi_var_create_nm +
  • gmi_var_delete +
  • gmi_var_evaluate_expression +
  • gmi_var_info_expression +
  • gmi_var_info_num_children +
  • gmi_var_info_type +
  • gmi_var_list_children +
  • gmi_var_set_format +
  • gmi_var_show_attributes +
  • gmi_var_show_format +
  • gmi_var_update +
  • + + + +


    + +

    Files

    + + +
  • breakpoint.c +
  • connect.c +
  • cpp_int.cc +
  • data_man.c +
  • error.c +
  • get_free_pty.c +
  • get_free_vt.c +
  • misc.c +
  • prg_control.c +
  • stack_man.c +
  • target_man.c +
  • thread.c +
  • var_obj.c +
  • + + +


    + +

    breakpoint.c

    + + + + +
  • gmi_break_delete +
  • gmi_break_insert +
  • gmi_break_insert_full +
  • gmi_break_insert_full_fl +
  • gmi_break_set_condition +
  • gmi_break_set_times +
  • gmi_break_state +
  • gmi_break_watch +
  • + + + +


    + +

    connect.c

    + + + + +
  • gmi_end_aux_term +
  • gmi_start_xterm +
  • mi_connect_local +
  • mi_disconnect +
  • mi_force_version +
  • mi_get_workaround +
  • mi_set_workaround +
  • + + + +


    + +

    cpp_int.cc

    + + + + +
  • BreakDelete (MIDebugger) +
  • Breakpoint (MIDebugger) +
  • Breakpoint (MIDebugger) <1> +
  • BreakpointFull (MIDebugger) +
  • CallStack (MIDebugger) +
  • Connect (MIDebugger) +
  • Continue (MIDebugger) +
  • Disconnect (MIDebugger) +
  • EvalExpression (MIDebugger) +
  • FillTypeVal (MIDebugger) +
  • FinishFun (MIDebugger) +
  • GoTo (MIDebugger) +
  • GoTo (MIDebugger) <1> +
  • Kill (MIDebugger) +
  • MIDebugger (MIDebugger) +
  • ModifyExpression (MIDebugger) +
  • Poll (MIDebugger) +
  • ReturnNow (MIDebugger) +
  • Run (MIDebugger) +
  • RunOrContinue (MIDebugger) +
  • RunToMain (MIDebugger) +
  • SelectTargetLinux (MIDebugger) +
  • SelectTargetPID (MIDebugger) +
  • SelectTargetRemote (MIDebugger) +
  • SelectTargetTTY (MIDebugger) +
  • SelectTargetX11 (MIDebugger) +
  • Send (MIDebugger) +
  • StepOver (MIDebugger) +
  • Stop (MIDebugger) +
  • TargetUnselect (MIDebugger) +
  • TraceInto (MIDebugger) +
  • WatchDelete (MIDebugger) +
  • Watchpoint (MIDebugger) +
  • ~MIDebugger (MIDebugger) +
  • + + + +


    + +

    data_man.c

    + + + + +
  • gmi_data_evaluate_expression +
  • gmi_dir +
  • + + + +


    + +

    error.c

    + + + + +
  • mi_get_error_str +
  • + + + +


    + +

    get_free_pty.c

    + + + + +
  • gmi_end_pty +
  • gmi_look_for_free_pty +
  • mi_look_for_free_pty +
  • + + + +


    + +

    get_free_vt.c

    + + + + +
  • gmi_look_for_free_vt +
  • mi_look_for_free_vt +
  • + + + +


    + +

    misc.c

    + + + + +
  • gmi_gdb_exit +
  • gmi_gdb_set +
  • gmi_gdb_show +
  • gmi_gdb_version +
  • + + + +


    + +

    prg_control.c

    + + + + +
  • gmi_exec_continue +
  • gmi_exec_finish +
  • gmi_exec_interrupt +
  • gmi_exec_kill +
  • gmi_exec_next +
  • gmi_exec_next_cnt +
  • gmi_exec_next_instruction +
  • gmi_exec_return +
  • gmi_exec_run +
  • gmi_exec_step +
  • gmi_exec_step_cnt +
  • gmi_exec_step_instruction +
  • gmi_exec_until +
  • gmi_exec_until_addr +
  • gmi_file_symbol_file +
  • gmi_set_exec +
  • gmi_target_terminal +
  • + + + +


    + +

    stack_man.c

    + + + + +
  • gmi_stack_info_depth +
  • gmi_stack_info_depth_get +
  • gmi_stack_info_frame +
  • gmi_stack_list_arguments +
  • gmi_stack_list_arguments_r +
  • gmi_stack_list_frames +
  • gmi_stack_list_frames_r +
  • gmi_stack_list_locals +
  • gmi_stack_select_frame +
  • + + + +


    + +

    target_man.c

    + + + + +
  • gmi_target_attach +
  • gmi_target_detach +
  • gmi_target_select +
  • + + + +


    + +

    thread.c

    + + + + +
  • gmi_thread_list_all_threads +
  • gmi_thread_list_ids +
  • gmi_thread_select +
  • + + + +


    + +

    var_obj.c

    + + + + +
  • gmi_full_var_create +
  • gmi_var_assign +
  • gmi_var_create +
  • gmi_var_create_nm +
  • gmi_var_delete +
  • gmi_var_evaluate_expression +
  • gmi_var_info_expression +
  • gmi_var_info_num_children +
  • gmi_var_info_type +
  • gmi_var_list_children +
  • gmi_var_set_format +
  • gmi_var_show_attributes +
  • gmi_var_show_format +
  • gmi_var_update +
  • + + + +


    + +

    Command

    + + +
  • -break-after +
  • -break-condition +
  • -break-delete +
  • -break-enable + -break-disable +
  • -break-insert +
  • -break-insert [ops] file:line +
  • -break-insert file:line +
  • -break-watch +
  • -data-evaluate-expression +
  • -environment-directory +
  • -exec-abort [using kill] +
  • -exec-continue +
  • -exec-finish +
  • -exec-interrupt [replacement] +
  • -exec-next +
  • -exec-next count +
  • -exec-next-instruction +
  • -exec-return +
  • -exec-run +
  • -exec-step +
  • -exec-step count +
  • -exec-step-instruction +
  • -exec-until +
  • -exec-until (using *address) +
  • -file-exec-and-symbols + -exec-arguments +
  • -file-symbol-file +
  • -gdb-exit +
  • -gdb-set +
  • -gdb-show +
  • -gdb-version +
  • -stack-info-depth +
  • -stack-info-depth [no args] +
  • -stack-info-frame [using frame] +
  • -stack-list-arguments +
  • -stack-list-frames +
  • -stack-list-locals +
  • -stack-select-frame +
  • -target-attach [using attach] +
  • -target-detach +
  • -target-select +
  • -thread-list-all-threads +
  • -thread-list-ids +
  • -thread-select +
  • -var-assign +
  • -var-create +
  • -var-create + -var-info-expression + -var-show-attributes +
  • -var-create [auto name] +
  • -var-delete +
  • -var-evaluate-expression +
  • -var-info-expression +
  • -var-info-num-children +
  • -var-info-type +
  • -var-list-children +
  • -var-set-format +
  • -var-show-attributes +
  • -var-show-format +
  • -var-update +
  • tty +
  • + + +


    + +

    -break-after

    + + + + +
  • gmi_break_set_times +
  • + + + +


    + +

    -break-condition

    + + + + +
  • gmi_break_set_condition +
  • + + + +


    + +

    -break-delete

    + + + + +
  • gmi_break_delete +
  • + + + +


    + +

    -break-enable + -break-disable

    + + + + +
  • gmi_break_state +
  • + + + +


    + +

    -break-insert

    + + + + +
  • gmi_break_insert_full +
  • + + + +


    + +

    -break-insert [ops] file:line

    + + + + +
  • gmi_break_insert_full_fl +
  • + + + +


    + +

    -break-insert file:line

    + + + + +
  • gmi_break_insert +
  • + + + +


    + +

    -break-watch

    + + + + +
  • gmi_break_watch +
  • + + + +


    + +

    -data-evaluate-expression

    + + + + +
  • gmi_data_evaluate_expression +
  • + + + +


    + +

    -environment-directory

    + + + + +
  • gmi_dir +
  • + + + +


    + +

    -exec-abort [using kill]

    + + + + +
  • gmi_exec_kill +
  • + + + +


    + +

    -exec-continue

    + + + + +
  • gmi_exec_continue +
  • + + + +


    + +

    -exec-finish

    + + + + +
  • gmi_exec_finish +
  • + + + +


    + +

    -exec-interrupt [replacement]

    + + + + +
  • gmi_exec_interrupt +
  • + + + +


    + +

    -exec-next

    + + + + +
  • gmi_exec_next +
  • + + + +


    + +

    -exec-next count

    + + + + +
  • gmi_exec_next_cnt +
  • + + + +


    + +

    -exec-next-instruction

    + + + + +
  • gmi_exec_next_instruction +
  • + + + +


    + +

    -exec-return

    + + + + +
  • gmi_exec_return +
  • + + + +


    + +

    -exec-run

    + + + + +
  • gmi_exec_run +
  • + + + +


    + +

    -exec-step

    + + + + +
  • gmi_exec_step +
  • + + + +


    + +

    -exec-step count

    + + + + +
  • gmi_exec_step_cnt +
  • + + + +


    + +

    -exec-step-instruction

    + + + + +
  • gmi_exec_step_instruction +
  • + + + +


    + +

    -exec-until

    + + + + +
  • gmi_exec_until +
  • + + + +


    + +

    -exec-until (using *address)

    + + + + +
  • gmi_exec_until_addr +
  • + + + +


    + +

    -file-exec-and-symbols + -exec-arguments

    + + + + +
  • gmi_set_exec +
  • + + + +


    + +

    -file-symbol-file

    + + + + +
  • gmi_file_symbol_file +
  • + + + +


    + +

    -gdb-exit

    + + + + +
  • gmi_gdb_exit +
  • + + + +


    + +

    -gdb-set

    + + + + +
  • gmi_gdb_set +
  • + + + +


    + +

    -gdb-show

    + + + + +
  • gmi_gdb_show +
  • + + + +


    + +

    -gdb-version

    + + + + +
  • gmi_gdb_version +
  • + + + +


    + +

    -stack-info-depth

    + + + + +
  • gmi_stack_info_depth +
  • + + + +


    + +

    -stack-info-depth [no args]

    + + + + +
  • gmi_stack_info_depth_get +
  • + + + +


    + +

    -stack-info-frame [using frame]

    + + + + +
  • gmi_stack_info_frame +
  • + + + +


    + +

    -stack-list-arguments

    + + + + +
  • gmi_stack_list_arguments +
  • gmi_stack_list_arguments_r +
  • + + + +


    + +

    -stack-list-frames

    + + + + +
  • gmi_stack_list_frames +
  • gmi_stack_list_frames_r +
  • + + + +


    + +

    -stack-list-locals

    + + + + +
  • gmi_stack_list_locals +
  • + + + +


    + +

    -stack-select-frame

    + + + + +
  • gmi_stack_select_frame +
  • + + + +


    + +

    -target-attach [using attach]

    + + + + +
  • gmi_target_attach +
  • + + + +


    + +

    -target-detach

    + + + + +
  • gmi_target_detach +
  • + + + +


    + +

    -target-select

    + + + + +
  • gmi_target_select +
  • + + + +


    + +

    -thread-list-all-threads

    + + + + +
  • gmi_thread_list_all_threads +
  • + + + +


    + +

    -thread-list-ids

    + + + + +
  • gmi_thread_list_ids +
  • + + + +


    + +

    -thread-select

    + + + + +
  • gmi_thread_select +
  • + + + +


    + +

    -var-assign

    + + + + +
  • gmi_var_assign +
  • + + + +


    + +

    -var-create

    + + + + +
  • gmi_var_create_nm +
  • + + + +


    + +

    -var-create + -var-info-expression + -var-show-attributes

    + + + + +
  • gmi_full_var_create +
  • + + + +


    + +

    -var-create [auto name]

    + + + + +
  • gmi_var_create +
  • + + + +


    + +

    -var-delete

    + + + + +
  • gmi_var_delete +
  • + + + +


    + +

    -var-evaluate-expression

    + + + + +
  • gmi_var_evaluate_expression +
  • + + + +


    + +

    -var-info-expression

    + + + + +
  • gmi_var_info_expression +
  • + + + +


    + +

    -var-info-num-children

    + + + + +
  • gmi_var_info_num_children +
  • + + + +


    + +

    -var-info-type

    + + + + +
  • gmi_var_info_type +
  • + + + +


    + +

    -var-list-children

    + + + + +
  • gmi_var_list_children +
  • + + + +


    + +

    -var-set-format

    + + + + +
  • gmi_var_set_format +
  • + + + +


    + +

    -var-show-attributes

    + + + + +
  • gmi_var_show_attributes +
  • + + + +


    + +

    -var-show-format

    + + + + +
  • gmi_var_show_format +
  • + + + +


    + +

    -var-update

    + + + + +
  • gmi_var_update +
  • + + + +


    + +

    tty

    + + + + +
  • gmi_target_terminal +
  • + + + + + +


    + +
    gmi_break_insert (breakpoint.c 133)
    + +Syntax

    + + + +

    + gmi_break_insert(mi_h *h, const char *file, int line);
    +
    + + + + +Description

    + +Insert a breakpoint at file:line. +

    + + +Return Value

    + +A new mi_bkpt structure with info about the breakpoint. NULL on +error. +

    + + + +


    + +
    gmi_break_insert_full (breakpoint.c 150)
    + +Syntax

    + + + +

    + gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, const char *cond, int count, int thread, const char *where);
    +
    + + + + +Description

    + +Insert a breakpoint, all available options. +

    + + +Return Value

    + +A new mi_bkpt structure with info about the breakpoint. NULL on +error. +

    + + + +


    + +
    gmi_break_insert_full_fl (breakpoint.c 169)
    + +Syntax

    + + + +

    + gmi_break_insert_full_fl(mi_h *h, const char *file, int line, int temporary, int hard_assist, const char *cond, int count, int thread);
    +
    + + + + +Description

    + +Insert a breakpoint, all available options. +

    + + +Return Value

    + +A new mi_bkpt structure with info about the breakpoint. NULL on +error. +

    + + + +


    + +
    gmi_break_delete (breakpoint.c 188)
    + +Syntax

    + + + +

    + gmi_break_delete(mi_h *h, int number);
    +
    + + + + +Description

    + +Remove a breakpoint. +

    + + +Return Value

    + +!=0 OK. Note that gdb always says OK, but errors can be sent to the +console. +

    + + + +


    + +
    gmi_break_set_times (breakpoint.c 205)
    + +Syntax

    + + + +

    + gmi_break_set_times(mi_h *h, int number, int count);
    +
    + + + + +Description

    + +Modify the "ignore" count for a breakpoint. +

    + + +Return Value

    + +!=0 OK. Note that gdb always says OK, but errors can be sent to the +console. +

    + + + +


    + +
    gmi_break_set_condition (breakpoint.c 221)
    + +Syntax

    + + + +

    + gmi_break_set_condition(mi_h *h, int number, const char *condition);
    +
    + + + + +Description

    + +Associate a condition with the breakpoint. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_break_state (breakpoint.c 238)
    + +Syntax

    + + + +

    + gmi_break_state(mi_h *h, int number, int enable);
    +
    + + + + +Description

    + +Enable or disable a breakpoint. +

    + + +Return Value

    + +!=0 OK. Note that gdb always says OK, but errors can be sent to the +console. +

    + + + +


    + +
    gmi_break_watch (breakpoint.c 258)
    + +Syntax

    + + + +

    + gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp);
    +
    + + + + +Description

    + +Set a watchpoint. It doesn't work for remote targets! +

    + + +Return Value

    + +A new mi_wp structure with info about the watchpoint. NULL on +error. +

    + + + +


    + +
    mi_connect_local (connect.c 330)
    + +Syntax

    + + + +

    + mi_connect_local();
    +
    + + + + +Description

    + +Connect to a local copy of gdb. Note that the mi_h structure is something +similar to a "FILE *" for stdio. +

    + + +Return Value

    + +A new mi_h structure or NULL on error. +

    + + + +


    + +
    mi_disconnect (connect.c 410)
    + +Syntax

    + + + +

    + mi_disconnect(mi_h *h);
    +
    + + + + +Description

    + +Close connection. You should ask gdb to quit first gmi_gdb_exit. +

    + + + + +


    + +
    gmi_start_xterm (connect.c 647)
    + +Syntax

    + + + +

    + gmi_start_xterm();
    +
    + + + + +Description

    + +Opens a new xterm to be used by the child process to debug. +

    + + +Return Value

    + +A new mi_aux_term structure, you can use gmi_end_aux_term to +release it. +

    + + + +


    + +
    gmi_end_aux_term (connect.c 764)
    + +Syntax

    + + + +

    + gmi_end_aux_term(mi_aux_term *t);
    +
    + + + + +Description

    + +Closes the auxiliar terminal and releases the allocated memory. +

    + + + + +


    + +
    mi_force_version (connect.c 781)
    + +Syntax

    + + + +

    + mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, unsigned vMinor);
    +
    + + + + +Description

    + +Forces the MI version. Currently the library can't detect it so you must +force it manually. GDB 5.x implemented MI v1 and 6.x v2. +

    + + + + +


    + +
    mi_set_workaround (connect.c 794)
    + +Syntax

    + + + +

    + mi_set_workaround(unsigned wa, int enable);
    +
    + + + + +Description

    + +Dis/Enables the @var{wa} workaround for a bug in gdb. +

    + + + + +


    + +
    mi_get_workaround (connect.c 813)
    + +Syntax

    + + + +

    + mi_get_workaround(unsigned wa);
    +
    + + + + +Description

    + +Finds if the @var{wa} workaround for a bug in gdb is enabled. +

    + + +Return Value

    + +!=0 if enabled. +

    + + + +


    + +
    MIDebugger (cpp_int.cc 22)
    + +Syntax

    + + + +

    + MIDebugger::MIDebugger();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Initializes a debugger object. It starts in the "disconnected" state. +Use Connect after it. +

    + + + + +


    + +
    ~MIDebugger (cpp_int.cc 40)
    + +Syntax

    + + + +

    + MIDebugger::~MIDebugger();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +This is the destructor for the class. It tries to change the state to +"disconnected" doing the needed actions. +

    + + + + +


    + +
    Connect (cpp_int.cc 79)
    + +Syntax

    + + + +

    + MIDebugger::Connect(bool);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Connects to gdb. Currently only local connections are supported, that's +a gdb limitation. Call it when in "unconnected" state, on success it will +change to the "connected" state. After it you should call one of the +SelectTarget members. SelectTargetX11, SelectTargetLinux or +SelectTargetRemote. To finish the connection use Disconnect. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    Disconnect (cpp_int.cc 105)
    + +Syntax

    + + + +

    + MIDebugger::Disconnect();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Finishes the connection to gdb. Call when in "connected" state, on success +it will change to "disconnected" state. This function first tries to exit +from gdb and then close the connection. But if gdb fails to exit it will be +killed. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    SelectTargetTTY (cpp_int.cc 127)
    + +Syntax

    + + + +

    + MIDebugger::SelectTargetTTY(const char *exec, const char *args, const char *auxtty, dMode m);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Protected member that implements SelectTargetX11 and +SelectTargetLinux. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    SelectTargetX11 (cpp_int.cc 180)
    + +Syntax

    + + + +

    + MIDebugger::SelectTargetX11(const char *exec, const char *args, const char *auxtty);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts a debug session for X11. It opens an xterm console for the program +to debug and tells gdb which executable to debug and the command line +options to pass. You can specify an already existing tty console to be used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use Run or use the members +to define breakpoints and similar stuff. To finish it use +TargetUnselect. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    SelectTargetLinux (cpp_int.cc 202)
    + +Syntax

    + + + +

    + MIDebugger::SelectTargetLinux(const char *exec, const char *args, const char *auxtty);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts a debug session for Linux console. It selects an empty VT for the +program to debug and tells gdb which executable to debug and the command line +options to pass. You can specify an already existing tty console to be used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use Run or use the members +to define breakpoints and similar stuff. To finish it use +TargetUnselect. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    SelectTargetRemote (cpp_int.cc 228)
    + +Syntax

    + + + +

    + MIDebugger::SelectTargetRemote(const char *exec, const char *rparams, const char *rtype);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts a remote session. The other end should be running gdbserver. You +must specify a local copy of the program to debug with debug info. The remote +copy can be stripped. The @var{rtype} and @var{rparams} selects the protocol +and the remote machine. Read gdb docs to know more about the available +options. If @var{rtype} is omitted "extended-remote" protocol is used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use Run or use the members +to define breakpoints and similar stuff. To finish it use +TargetUnselect. Note that when gdb uses remote debugging the remote +program starts running. The Run member knows about it. +

    + + +Return Value

    + +!=0 OK. +

    + + +Example

    + +o->SelectTargetRemote("./exec_file","192.168.1.65:5000"); +

    + + +


    + +
    SelectTargetPID (cpp_int.cc 258)
    + +Syntax

    + + + +

    + MIDebugger::SelectTargetPID(const char *exec, int pid);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts a local session using an already running process. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    TargetUnselect (cpp_int.cc 295)
    + +Syntax

    + + + +

    + MIDebugger::TargetUnselect();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Used to unselect the current target. When X11 mode it closes the auxiliar +terminal. For remote debugging it uses "detach". Can be called when in +"target_specified" state. On success it changes to "connected" state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    Run (cpp_int.cc 335)
    + +Syntax

    + + + +

    + MIDebugger::Run();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts running the program. You should set breakpoint before it. Can be +called when state is "target_specified". On success will change to "running" +state. After it you should poll for async responses using Poll. The +program can stop for many reasons asynchronously and also exit. This +information is known using Poll. You can stop the program using Stop. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    Stop (cpp_int.cc 365)
    + +Syntax

    + + + +

    + MIDebugger::Stop();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Stops the program execution. GDB sends an interrupt signal to the program. +Can be called when the state is "running". It won't switch to "stopped" +state automatically. Instead you must poll for async events and wait for a +stopped notification. After it you can call Continue to resume +execution. +

    + + +Return Value

    + + +

    + + +Example

    + +!=0 OK +

    + + +


    + +
    Poll (cpp_int.cc 386)
    + +Syntax

    + + + +

    + MIDebugger::Poll(mi_stop *&rs);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Polls gdb looking for async responses. Currently it just looks for +"stopped" messages. You must call it when the state is "running". But the +function will poll gdb even if the state isn't "running". When a stopped +message is received the state changes to stopped or target_specified (the +last is when we get some exit). +

    + + +Return Value

    + +!=0 if we got a response. The @var{rs} pointer will point to an +mi_stop structure if we got it or will be NULL if we didn't. +

    + + + +


    + +
    Continue (cpp_int.cc 432)
    + +Syntax

    + + + +

    + MIDebugger::Continue();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Resumes execution after the program "stopped". Can be called when the state +is stopped. On success will change to "running" state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    RunOrContinue (cpp_int.cc 454)
    + +Syntax

    + + + +

    + MIDebugger::RunOrContinue();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Starts program execution or resumes it. When the state is target_specified +it calls Run otherwise it uses Continue. Can be called when the +state is "target_specified" or "stopped". On success will change to +"running" state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    Kill (cpp_int.cc 473)
    + +Syntax

    + + + +

    + MIDebugger::Kill();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Kills the program you are debugging. Can be called when the state is +"stopped" or "running". On success changes the state to "target_specified". +Note that if you want to restart the program you can just call Run and +if you want to just stop the program call Stop. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    Breakpoint (cpp_int.cc 520)
    + +Syntax

    + + + +

    + MIDebugger::Breakpoint(const char *file, int line);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Inserts a breakpoint at @var{file} and @var{line}. Can be called when the +state is "stopped" or "target_specified". +

    + + +Return Value

    + +An mi_bkpt structure or NULL if error. +

    + + + +


    + +
    Breakpoint (cpp_int.cc 537)
    + +Syntax

    + + + +

    + MIDebugger::Breakpoint(const char *where, bool temporary, const char *cond, int count, int thread, bool hard_assist);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Inserts a breakpoint at @var{where}, all options available. Can be called +when the state is "stopped" or "target_specified". +

    + + +Return Value

    + +An mi_bkpt structure or NULL if error. +

    + + + +


    + +
    BreakpointFull (cpp_int.cc 585)
    + +Syntax

    + + + +

    + MIDebugger::BreakpointFull(const char *file, int line, bool temporary, const char *cond, int count, int thread, bool hard_assist);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Inserts a breakpoint at @var{file} and @var{line} all options available. +Can be called when the state is "stopped" or "target_specified". +

    + + +Return Value

    + +An mi_bkpt structure or NULL if error. +

    + + + +


    + +
    BreakDelete (cpp_int.cc 605)
    + +Syntax

    + + + +

    + MIDebugger::BreakDelete(mi_bkpt *b);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Removes the specified breakpoint. It doesn't free the structure. Can be +called when the state is "stopped" or "target_specified". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    Watchpoint (cpp_int.cc 622)
    + +Syntax

    + + + +

    + MIDebugger::Watchpoint(enum mi_wp_mode mode, const char *exp);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Inserts a watchpoint for the specified expression. Can be called when the +state is "stopped" or "target_specified". +

    + + +Return Value

    + +An mi_wp structure or NULL if error. +

    + + + +


    + +
    WatchDelete (cpp_int.cc 639)
    + +Syntax

    + + + +

    + MIDebugger::WatchDelete(mi_wp *w);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Removes the specified watchpoint. It doesn't free the structure. Can be +called when the state is "stopped" or "target_specified". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    RunToMain (cpp_int.cc 657)
    + +Syntax

    + + + +

    + MIDebugger::RunToMain();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Puts a temporal breakpoint in main function and starts running. Can be +called when the state is "target_specified". If successful the state will +change to "running". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    StepOver (cpp_int.cc 681)
    + +Syntax

    + + + +

    + MIDebugger::StepOver(bool inst);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Executes upto the next line, doesn't follow function calls. The @var{inst} +argument is for assembler. If the state is "target_specified" it will go to +the first line in the main function. If the state is "stopped" will use the +next command. If successfully the state will change to "running". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    GoTo (cpp_int.cc 713)
    + +Syntax

    + + + +

    + MIDebugger::GoTo(const char *file, int line);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Executes until the specified point. If the state is "target_specified" it +uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. +Fails for any other state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    GoTo (cpp_int.cc 751)
    + +Syntax

    + + + +

    + MIDebugger::GoTo(void *addr);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Executes until the specified point. If the state is "target_specified" it +uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. +Fails for any other state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    FinishFun (cpp_int.cc 788)
    + +Syntax

    + + + +

    + MIDebugger::FinishFun();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Resumes execution until the end of the current funtion is reached. Only +usable when we are in the "stopped" state. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    ReturnNow (cpp_int.cc 808)
    + +Syntax

    + + + +

    + MIDebugger::ReturnNow();
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + + +

    + + +Return Value

    + +!=NULL OK, the returned frame is the current location. That's a +synchronous function. +

    + + + +


    + +
    CallStack (cpp_int.cc 824)
    + +Syntax

    + + + +

    + MIDebugger::CallStack(bool args);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + + +

    + + +Return Value

    + +!=NULL OK, the list of frames is returned. +

    + + + +


    + +
    TraceInto (cpp_int.cc 860)
    + +Syntax

    + + + +

    + MIDebugger::TraceInto(bool inst);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Executes upto the next line, it follows function calls. The @var{inst} +argument is for assembler. If the state is "target_specified" it will go to +the first line in the main function. If the state is "stopped" will use the +next command. If successfully the state will change to "running". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    EvalExpression (cpp_int.cc 892)
    + +Syntax

    + + + +

    + MIDebugger::EvalExpression(const char *exp);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Evaluates the provided expression. If we get an error the error +description is returned instead. Can't be called if "disconnected" or +"running". +

    + + +Return Value

    + +The result of the expression (use free) or NULL. +

    + + + +


    + +
    ModifyExpression (cpp_int.cc 918)
    + +Syntax

    + + + +

    + MIDebugger::ModifyExpression(char *exp, char *newVal);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Modifies the provided expression. If we get an error the error +description is returned instead. Can't be called if "disconnected" or +"running". +

    + + +Return Value

    + +The result of the expression (use free) or NULL. +

    + + + +


    + +
    Send (cpp_int.cc 950)
    + +Syntax

    + + + +

    + MIDebugger::Send(const char *command);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Sends a command to gdb. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    FillTypeVal (cpp_int.cc 970)
    + +Syntax

    + + + +

    + MIDebugger::FillTypeVal(mi_gvar *var);
    +
    + + +Member of the class: +MIDebugger +

    + + + +Description

    + +Fills the type and value fields of the mi_gvar provided list. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_data_evaluate_expression (data_man.c 163)
    + +Syntax

    + + + +

    + gmi_data_evaluate_expression(mi_h *h, const char *expression);
    +
    + + + + +Description

    + +Evaluate an expression. Returns a parsed tree. +

    + + +Return Value

    + +The resulting value (as plain text) or NULL on error. +

    + + + +


    + +
    gmi_dir (data_man.c 180)
    + +Syntax

    + + + +

    + gmi_dir(mi_h *h, const char *path);
    +
    + + + + +Description

    + +Path for sources. You must use it to indicate where are the sources for +the program to debug. Only the MI v1 implementation is available. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    mi_get_error_str (error.c 10)
    + +Syntax

    + + + +

    + mi_get_error_str();
    +
    + + + + + + + +


    + +
    mi_look_for_free_pty (get_free_pty.c 41)
    + +Syntax

    + + + +

    + mi_look_for_free_pty(int *master, char **slave);
    +
    + + + + +Description

    + +Look for a free and usable pseudo terminal. Low level, use +gmi_look_for_free_pty. +

    + + +Return Value

    + +A file descriptor connected to the master pty and the name of the slave device, or <0 on error. +

    + + + +


    + +
    gmi_look_for_free_pty (get_free_pty.c 92)
    + +Syntax

    + + + +

    + gmi_look_for_free_pty();
    +
    + + + + +Description

    + +Look for a free and usable pseudo terminal to be used by the child. +

    + + +Return Value

    + +A new mi_pty structure, you can use gmi_end_pty to +release it. +

    + + + +


    + +
    gmi_end_pty (get_free_pty.c 124)
    + +Syntax

    + + + +

    + gmi_end_pty(mi_pty *p);
    +
    + + + + +Description

    + +Closes the pseudo termial master and releases the allocated memory. +

    + + + + +


    + +
    mi_look_for_free_vt (get_free_vt.c 51)
    + +Syntax

    + + + +

    + mi_look_for_free_vt() 53;
    +
    + + + + +Description

    + +Look for a free and usable Linux VT. Low level, use +gmi_look_for_free_vt. +

    + + +Return Value

    + +The VT number or <0 on error. +

    + + + +


    + +
    gmi_look_for_free_vt (get_free_vt.c 135)
    + +Syntax

    + + + +

    + gmi_look_for_free_vt() 137;
    +
    + + + + +Description

    + +Look for a free and usable Linux VT to be used by the child. +

    + + +Return Value

    + +A new mi_aux_term structure, you can use gmi_end_aux_term to +release it. +

    + + + +


    + +
    gmi_gdb_exit (misc.c 54)
    + +Syntax

    + + + +

    + gmi_gdb_exit(mi_h *h);
    +
    + + + + +Description

    + +Exit gdb killing the child is it is running. +

    + + + + +


    + +
    gmi_gdb_version (misc.c 70)
    + +Syntax

    + + + +

    + gmi_gdb_version(mi_h *h);
    +
    + + + + +Description

    + +Send the version to the console. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_gdb_set (misc.c 86)
    + +Syntax

    + + + +

    + gmi_gdb_set(mi_h *h, const char *var, const char *val);
    +
    + + + + +Description

    + +Set a gdb variable. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_gdb_show (misc.c 102)
    + +Syntax

    + + + +

    + gmi_gdb_show(mi_h *h, const char *var);
    +
    + + + + +Description

    + +Get a gdb variable. +

    + + +Return Value

    + +The current value of the variable or NULL on error. +

    + + + +


    + +
    gmi_set_exec (prg_control.c 172)
    + +Syntax

    + + + +

    + gmi_set_exec(mi_h *h, const char *file, const char *args);
    +
    + + + + +Description

    + +Specify the executable and arguments for local debug. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_run (prg_control.c 193)
    + +Syntax

    + + + +

    + gmi_exec_run(mi_h *h);
    +
    + + + + +Description

    + +Start running the executable. Remote sessions starts running. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_continue (prg_control.c 209)
    + +Syntax

    + + + +

    + gmi_exec_continue(mi_h *h);
    +
    + + + + +Description

    + +Continue the execution after a "stop". +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_target_terminal (prg_control.c 226)
    + +Syntax

    + + + +

    + gmi_target_terminal(mi_h *h, const char *tty_name);
    +
    + + + + +Description

    + +Indicate which terminal will use the target program. For local sessions. +

    + + +Return Value

    + +!=0 OK +

    + + +Example

    + + +

    + + +


    + +
    gmi_file_symbol_file (prg_control.c 242)
    + +Syntax

    + + + +

    + gmi_file_symbol_file(mi_h *h, const char *file);
    +
    + + + + +Description

    + +Specify what's the local copy that have debug info. For remote sessions. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_finish (prg_control.c 259)
    + +Syntax

    + + + +

    + gmi_exec_finish(mi_h *h);
    +
    + + + + +Description

    + +Continue until function return, the return value is included in the async +response. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    gmi_exec_interrupt (prg_control.c 278)
    + +Syntax

    + + + +

    + gmi_exec_interrupt(mi_h *h);
    +
    + + + + +Description

    + +Stop the program using SIGINT. The corresponding command should be +-exec-interrupt but not even gdb 6.1.1 can do it because the "async" mode +isn't really working. +

    + + +Return Value

    + +Always 1 +

    + + +Example

    + + +

    + + +


    + +
    gmi_exec_next (prg_control.c 299)
    + +Syntax

    + + + +

    + gmi_exec_next(mi_h *h);
    +
    + + + + +Description

    + +Next line of code. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_next_cnt (prg_control.c 315)
    + +Syntax

    + + + +

    + gmi_exec_next_cnt(mi_h *h, int count);
    +
    + + + + +Description

    + +Skip count lines of code. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_next_instruction (prg_control.c 331)
    + +Syntax

    + + + +

    + gmi_exec_next_instruction(mi_h *h);
    +
    + + + + +Description

    + +Next line of assembler code. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_step (prg_control.c 347)
    + +Syntax

    + + + +

    + gmi_exec_step(mi_h *h);
    +
    + + + + +Description

    + +Next line of code. Get inside functions. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_step_cnt (prg_control.c 363)
    + +Syntax

    + + + +

    + gmi_exec_step_cnt(mi_h *h, int count);
    +
    + + + + +Description

    + +Next count lines of code. Get inside functions. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_step_instruction (prg_control.c 379)
    + +Syntax

    + + + +

    + gmi_exec_step_instruction(mi_h *h);
    +
    + + + + +Description

    + +Next line of assembler code. Get inside calls. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_until (prg_control.c 396)
    + +Syntax

    + + + +

    + gmi_exec_until(mi_h *h, const char *file, int line);
    +
    + + + + +Description

    + +Execute until location is reached. If file is NULL then is until next +line. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_until_addr (prg_control.c 412)
    + +Syntax

    + + + +

    + gmi_exec_until_addr(mi_h *h, void *addr);
    +
    + + + + +Description

    + +Execute until location is reached. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_exec_return (prg_control.c 429)
    + +Syntax

    + + + +

    + gmi_exec_return(mi_h *h);
    +
    + + + + +Description

    + + +

    + + +Return Value

    + +A pointer to a new mi_frames structure indicating the current +location. NULL on error. +

    + + + +


    + +
    gmi_exec_kill (prg_control.c 447)
    + +Syntax

    + + + +

    + gmi_exec_kill(mi_h *h);
    +
    + + + + +Description

    + +Just kill the program. That's what -exec-abort should do, but it isn't +implemented by gdb. This implementation only works if the interactive mode +is disabled (gmi_gdb_set("confirm","off")). +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_stack_list_frames (stack_man.c 76)
    + +Syntax

    + + + +

    + gmi_stack_list_frames(mi_h *h);
    +
    + + + + +Description

    + +List of frames. Arguments aren't filled. +

    + + +Return Value

    + +A new list of mi_frames or NULL on error. +

    + + + +


    + +
    gmi_stack_list_frames_r (stack_man.c 93)
    + +Syntax

    + + + +

    + gmi_stack_list_frames_r(mi_h *h, int from, int to);
    +
    + + + + +Description

    + +List of frames. Arguments aren't filled. Only the frames in the @var{from} + - @var{to} range are returned. +

    + + +Return Value

    + +A new list of mi_frames or NULL on error. +

    + + + +


    + +
    gmi_stack_list_arguments (stack_man.c 109)
    + +Syntax

    + + + +

    + gmi_stack_list_arguments(mi_h *h, int show);
    +
    + + + + +Description

    + +List arguments. Only @var{level} and @var{args} filled. +

    + + +Return Value

    + +A new list of mi_frames or NULL on error. +

    + + + +


    + +
    gmi_stack_list_arguments_r (stack_man.c 126)
    + +Syntax

    + + + +

    + gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to);
    +
    + + + + +Description

    + +List arguments. Only @var{level} and @var{args} filled. Only for the +frames in the @var{from} - @var{to} range. +

    + + +Return Value

    + +A new list of mi_frames or NULL on error. +

    + + + +


    + +
    gmi_stack_info_frame (stack_man.c 142)
    + +Syntax

    + + + +

    + gmi_stack_info_frame(mi_h *h);
    +
    + + + + +Description

    + +Information about the current frame, including args. +

    + + +Return Value

    + +A new mi_frames or NULL on error. +

    + + + +


    + +
    gmi_stack_info_depth (stack_man.c 158)
    + +Syntax

    + + + +

    + gmi_stack_info_depth(mi_h *h, int max_depth);
    +
    + + + + +Description

    + +Stack info depth. +

    + + +Return Value

    + +The depth or -1 on error. +

    + + + +


    + +
    gmi_stack_info_depth_get (stack_man.c 184)
    + +Syntax

    + + + +

    + gmi_stack_info_depth_get(mi_h *h);
    +
    + + + + +Description

    + +Set stack info depth. +

    + + +Return Value

    + +The depth or -1 on error. +

    + + +Example

    + + +

    + + +


    + +
    gmi_stack_select_frame (stack_man.c 199)
    + +Syntax

    + + + +

    + gmi_stack_select_frame(mi_h *h, int framenum);
    +
    + + + + +Description

    + +Change current frame. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_stack_list_locals (stack_man.c 215)
    + +Syntax

    + + + +

    + gmi_stack_list_locals(mi_h *h, int show);
    +
    + + + + +Description

    + +List of local vars. +

    + + +Return Value

    + +A new mi_results tree containing the variables or NULL on error. +

    + + + +


    + +
    gmi_target_select (target_man.c 54)
    + +Syntax

    + + + +

    + gmi_target_select(mi_h *h, const char *type, const char *params);
    +
    + + + + +Description

    + +Connect to a remote gdbserver using the specified methode. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_target_attach (target_man.c 70)
    + +Syntax

    + + + +

    + gmi_target_attach(mi_h *h, pid_t pid);
    +
    + + + + +Description

    + +Attach to an already running process. +

    + + +Return Value

    + +The frame of the current location, NULL on error. +

    + + + +


    + +
    gmi_target_detach (target_man.c 87)
    + +Syntax

    + + + +

    + gmi_target_detach(mi_h *h);
    +
    + + + + +Description

    + +Detach from an attached process. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_thread_list_ids (thread.c 49)
    + +Syntax

    + + + +

    + gmi_thread_list_ids(mi_h *h, int **list);
    +
    + + + + +Description

    + +List available thread ids. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_thread_select (thread.c 65)
    + +Syntax

    + + + +

    + gmi_thread_select(mi_h *h, int id);
    +
    + + + + +Description

    + +Select a thread. +

    + + +Return Value

    + +A new mi_frames or NULL on error. +

    + + + +


    + +
    gmi_thread_list_all_threads (thread.c 82)
    + +Syntax

    + + + +

    + gmi_thread_list_all_threads(mi_h *h);
    +
    + + + + +Description

    + +Get a list of frames for each available thread. Implemented using "info +thread". +

    + + +Return Value

    + +A kist of frames, NULL on error +

    + + + +


    + +
    gmi_var_create_nm (var_obj.c 122)
    + +Syntax

    + + + +

    + gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp);
    +
    + + + + +Description

    + +Create a variable object. I recommend using gmi_var_create and letting +gdb choose the names. +

    + + +Return Value

    + +A new mi_gvar strcture or NULL on error. +

    + + + +


    + +
    gmi_var_create (var_obj.c 139)
    + +Syntax

    + + + +

    + gmi_var_create(mi_h *h, int frame, const char *exp);
    +
    + + + + +Description

    + +Create a variable object. The name is selected by gdb. Alternative: +gmi_full_var_create. +

    + + +Return Value

    + +A new mi_gvar strcture or NULL on error. +

    + + + +


    + +
    gmi_var_delete (var_obj.c 154)
    + +Syntax

    + + + +

    + gmi_var_delete(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Delete a variable object. Doesn't free the mi_gvar data. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_set_format (var_obj.c 170)
    + +Syntax

    + + + +

    + gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format);
    +
    + + + + +Description

    + +Set the format used to represent the result. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_show_format (var_obj.c 191)
    + +Syntax

    + + + +

    + gmi_var_show_format(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the format field with info from gdb. +

    + + +Return Value

    + +!=0 OK. +

    + + + +


    + +
    gmi_var_info_num_children (var_obj.c 207)
    + +Syntax

    + + + +

    + gmi_var_info_num_children(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the numchild field with info from gdb. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_info_type (var_obj.c 223)
    + +Syntax

    + + + +

    + gmi_var_info_type(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the type field with info from gdb. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_info_expression (var_obj.c 240)
    + +Syntax

    + + + +

    + gmi_var_info_expression(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the expression and lang fields with info from gdb. Note that lang +isn't filled during creation. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_show_attributes (var_obj.c 258)
    + +Syntax

    + + + +

    + gmi_var_show_attributes(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the attr field with info from gdb. Note that attr isn't filled +during creation. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_full_var_create (var_obj.c 275)
    + +Syntax

    + + + +

    + gmi_full_var_create(mi_h *h, int frame, const char *exp);
    +
    + + + + +Description

    + +Create the variable and also fill the lang and attr fields. The name is +selected by gdb. +

    + + +Return Value

    + +A new mi_gvar strcture or NULL on error. +

    + + + +


    + +
    gmi_var_update (var_obj.c 297)
    + +Syntax

    + + + +

    + gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed);
    +
    + + + + +Description

    + +Update variable. Use NULL for all. Note that *changed can be NULL if none +updated. +

    + + +Return Value

    + +!=0 OK. The @var{changed} list contains the list of changed vars. +

    + + + +


    + +
    gmi_var_assign (var_obj.c 313)
    + +Syntax

    + + + +

    + gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression);
    +
    + + + + +Description

    + +Change variable. The new value replaces the @var{value} field. +

    + + +Return Value

    + +!=0 OK +

    + + + +


    + +
    gmi_var_evaluate_expression (var_obj.c 337)
    + +Syntax

    + + + +

    + gmi_var_evaluate_expression(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +Fill the value field getting the current value for a variable. +

    + + +Return Value

    + +!=0 OK, value contains the result. +

    + + + +


    + +
    gmi_var_list_children (var_obj.c 362)
    + +Syntax

    + + + +

    + gmi_var_list_children(mi_h *h, mi_gvar *var);
    +
    + + + + +Description

    + +List children. It ONLY returns the first level information. :-(
    + On success the child field contains the list of children. +

    + + +Return Value

    + +!=0 OK +

    + + + + + + + diff --git a/examples/.cvsignore b/examples/.cvsignore new file mode 100644 index 0000000..ad49ccf --- /dev/null +++ b/examples/.cvsignore @@ -0,0 +1,15 @@ +*.dst +.*.dst +*.epr* +test_target +x11_test +remote_test +linux_test +x11_wp_test +x11_cpp_test +target_frames +x11_fr_test +pty_test +ticepic +*.cod +*.lst diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..0e6060c --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,31 @@ +#!/usr/bin/make + +all: test_target x11_test remote_test linux_test target_frames x11_fr_test \ + x11_wp_test x11_cpp_test pty_test + +CFLAGS=-O0 -Wall -gstabs+3 -I../src +CXXFLAGS=-O0 -Wall -gstabs+3 -I../src +LDLIBS= + +ticepic: ticepic.c ../src/libmigdb.a + +x11_test: x11_test.c ../src/libmigdb.a + +x11_cpp_test: x11_cpp_test.cc ../src/libmigdb.a + +x11_fr_test: x11_fr_test.c ../src/libmigdb.a + +x11_wp_test: x11_wp_test.c ../src/libmigdb.a + +remote_test: remote_test.c ../src/libmigdb.a + +linux_test: linux_test.c ../src/libmigdb.a + +pty_test: pty_test.c ../src/libmigdb.a + +clean: + -@rm *.o *.a test_target x11_test remote_test linux_test 2> /dev/null + -@rm x11_wp_test x11_cpp_test target_frames x11_fr_test 2> /dev/null + -@rm pty_test 2> /dev/null + + diff --git a/examples/cmds.txt b/examples/cmds.txt new file mode 100644 index 0000000..66cd5ca --- /dev/null +++ b/examples/cmds.txt @@ -0,0 +1,3 @@ +help +help break + diff --git a/examples/linux_test.c b/examples/linux_test.c new file mode 100644 index 0000000..3c610dd --- /dev/null +++ b/examples/linux_test.c @@ -0,0 +1,195 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + Linux console example/test of the libmigdb. + Run it from a Linux console. + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + res=0; + } + return res; +} + +int main(int argc, char *argv[]) +{ + mi_aux_term *child_vt=NULL; + mi_bkpt *bk; + mi_wp *wp; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + + /* You can use any gdb you want: */ + /*mi_set_gdb_exe("/usr/src/gdb-6.1.1/gdb/gdb");*/ + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Look for a free VT where we can run the child. */ + child_vt=gmi_look_for_free_vt(); + if (!child_vt) + printf("Error opening auxiliar terminal, we'll use current one.\n"); + else + { + printf("Free VT @ %s\n",child_vt->tty); + printf("\n\n***************************************\n"); + printf("Switch to the above mentioned terminal!\n"); + printf("***************************************\n\n\n"); + } + + /* Tell gdb to attach the child to a terminal. */ + if (!gmi_target_terminal(h,child_vt ? child_vt->tty : ttyname(STDIN_FILENO))) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./test_target","prb1 2 prb3")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Set a breakpoint. */ + bk=gmi_break_insert(h,"test_target.cc",12); + if (!bk) + { + printf("Error setting breakpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + + /* You can do things like: + gmi_break_delete(h,bk->number); + gmi_break_set_times(h,bk->number,2); + gmi_break_set_condition(h,bk->number,"1"); + gmi_break_state(h,bk->number,0);*/ + /* If we no longer need this data we can release it. */ + mi_free_bkpt(bk); + + /* Set a watchpoint, that's a data breakpoint. */ + wp=gmi_break_watch(h,wm_write,"v"); + if (!wp) + { + printf("Error al setting watchpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Watchpoint %d for expression: %s\n",wp->number,wp->exp); + mi_free_wp(wp); + + /* Run the program. */ + if (!gmi_exec_run(h)) + { + printf("Error in run!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be stopped at the breakpoint. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Continue execution. */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be terminated. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + gmi_end_aux_term(child_vt); + + return 0; +} diff --git a/examples/pty_test.c b/examples/pty_test.c new file mode 100644 index 0000000..070a3b5 --- /dev/null +++ b/examples/pty_test.c @@ -0,0 +1,141 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + Example/test of pseudo terminals. + Note: Contributed by Greg Watson (gwatson lanl gov) + +***************************************************************************/ + +#include +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); +} + +int main(int argc, char *argv[]) +{ + mi_pty *pty=NULL; + mi_h *h; + fd_set rfds; + char buf[BUFSIZ]; + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Look for a free pseudo terminal. */ + pty=gmi_look_for_free_pty(); + if (!pty) + { + printf("Error opening pseudo terminal.\n"); + return 1; + } + + printf("Free pty slave = %s, master on %d\n",pty->slave,pty->master); + + /* Tell gdb to attach the terminal. */ + if (!gmi_target_terminal(h,pty->slave)) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./test_target","prb1 2 prb3")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Run the program. */ + if (!gmi_exec_run(h)) + { + printf("Error in run!\n"); + mi_disconnect(h); + return 1; + } + + for (;;) + { + FD_ZERO(&rfds); + FD_SET(pty->master,&rfds); + FD_SET(0,&rfds); + + if (select(pty->master+1,&rfds,NULL,NULL,NULL)<0) + { + perror("select"); + mi_disconnect(h); + return 1; + } + + if (FD_ISSET(pty->master,&rfds)) + { + int n=read(pty->master,buf,BUFSIZ); + if (n<=0) + break; + write(1,buf,n); + } + + if (FD_ISSET(0,&rfds)) + { + int n=read(0,buf,BUFSIZ); + if (n<=0) + break; + write(pty->master,buf,n); + } + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + gmi_end_pty(pty); + + return 0; +} diff --git a/examples/remote_test.c b/examples/remote_test.c new file mode 100644 index 0000000..9e76969 --- /dev/null +++ b/examples/remote_test.c @@ -0,0 +1,179 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + Remote example/test of the libmigdb. + To run it you must: + +1) Copy "test_target" to the "remote" host (it could be the same machine if +you want). +2) In the "remote" end run "gdbserver :6000 ./test_target 1 2 3" +3) Run this example in the "local" end. + +You should edit the remote IP and port before compiling. + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +// Example: "192.168.1.3:5500" +#define REMOTE_MACHINE ":6000" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +int wait_for_stop(mi_h *h) +{ + mi_output *o; + int res=1; + char *aux; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + o=mi_retire_response(h); + if (mi_get_async_stop_reason(o,&aux)) + printf("Stopped, reason: %s\n",aux); + else + { + printf("Error while waiting: %s\n",aux); + res=0; + } + mi_free_output(o); + return res; +} + +int main(int argc, char *argv[]) +{ + mi_bkpt *bk; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + + /* You can use any gdb you want: */ + /*mi_set_gdb_exe("/usr/src/gdb-6.1.1/gdb/gdb");*/ + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Tell gdb to load symbols from the local copy. */ + if (!gmi_file_symbol_file(h,"./test_target")) + { + printf("Error loading symbols\n"); + mi_disconnect(h); + return 1; + } + + /* Connect to remote machine using TCP/IP. */ + if (!gmi_target_select(h,"extended-remote",REMOTE_MACHINE)) + { + printf("Error connecting to gdb server\n"); + mi_disconnect(h); + return 1; + } + + /* Set a breakpoint. */ + bk=gmi_break_insert(h,"test_target.cc",12); + if (!bk) + { + printf("Error setting breakpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + + /* You can do things like: + gmi_break_delete(h,bk->number); + gmi_break_set_times(h,bk->number,2); + gmi_break_set_condition(h,bk->number,"1"); + gmi_break_state(h,bk->number,0);*/ + /* If we no longer need this data we can release it. */ + mi_free_bkpt(bk); + + /* Run the program. */ + /* Note that remote targets starts running and we must use continue! */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be stopped at the breakpoint. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Continue execution. */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be terminated. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + + return 0; +} diff --git a/examples/target_frames.cc b/examples/target_frames.cc new file mode 100644 index 0000000..e58bbab --- /dev/null +++ b/examples/target_frames.cc @@ -0,0 +1,23 @@ +#include +#include + +int v=2; struct { char *a; int q; } sup; + +int FuncLev2(char *str) +{ + v=5; + return strlen(str); +} + +int FuncLev1(char *s, int i) +{ + i+=FuncLev2(s); + return i; +} + +int main(int argc, char *argv[]) +{ + FuncLev1("Hi!",2500); + printf("v=%d\n",v); + return 0; +} diff --git a/examples/test_target.cc b/examples/test_target.cc new file mode 100644 index 0000000..83deeb6 --- /dev/null +++ b/examples/test_target.cc @@ -0,0 +1,22 @@ +#include + +int v=3; + +void PrintHi(int val) +{ + int x=val*3; + + printf("Hi! x=%d\n",x); v++; +} + +int main(int argc, char *argv[]) +{ + printf("arguments: %d\n",argc); + for (int i=0; i +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +void print_frames(mi_frames *f, int free_f) +{ + mi_frames *ff=f; + + if (!f) + { + printf("Error! empty frames info\n"); + return; + } + while (f) + { + printf("Level %d, addr %p, func %s, where: %s:%d args? %c\n",f->level,f->addr, + f->func,f->file,f->line,f->args ? 'y' : 'n'); + f=f->next; + } + if (free_f) + mi_free_frames(ff); +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + print_frames(sr->frame,0); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + res=0; + } + return res; +} + +void print_gvar(mi_gvar *v) +{ + if (!v) + { + printf("Error! failed to define variable\n"); + return; + } + printf("Variable name: '%s', type: '%s', number of children: %d format: %s expression: %s lang: %s editable: %c\n", + v->name,v->type,v->numchild,mi_format_enum_to_str(v->format), + v->exp,mi_lang_enum_to_str(v->lang),v->attr & MI_ATTR_EDITABLE ? 'y' : 'n'); +} + +void print_update(mi_gvar_chg *changed) +{ + printf("List of changed variables:\n"); + while (changed) + { + printf("Name: %s\nIn scope: %c\n",changed->name,changed->in_scope ? 'y' : 'n'); + if (changed->in_scope && changed->new_type) + { + printf("New type: %s\nNew num children: %d\n",changed->new_type,changed->new_num_children); + } + changed=changed->next; + printf("\n"); + } +} + +void print_children(mi_gvar *ch) +{ + int i; + mi_gvar *s; + + if (!ch->child) + { + printf("Error! getting children list\n"); + return; + } + printf("\nChildren List (%d):\n",ch->numchild); + for (i=0, s=ch->child; inumchild; s++, i++) + { + printf("Name: %s Exp: %s Children: %d",s->name,s->exp,s->numchild); + if (s->type) + printf(" Type: %s",s->type); + if (s->value) + printf(" Value: %s",s->value); + printf("\n"); + } + printf("\n"); +} + + +int main(int argc, char *argv[]) +{ + mi_aux_term *xterm_tty=NULL; + mi_bkpt *bk; + mi_frames *fr; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + mi_gvar *gv, *gv2; + mi_gvar_chg *changed; + char *value; + int r_assign; + + /* You can use any gdb you want: */ + mi_set_gdb_exe("./icepic"); + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./test_bcd.cod","")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + sleep(5); + gmi_end_aux_term(xterm_tty); + + return 0; +} diff --git a/examples/x11_cpp_test.cc b/examples/x11_cpp_test.cc new file mode 100644 index 0000000..7d524cc --- /dev/null +++ b/examples/x11_cpp_test.cc @@ -0,0 +1,166 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + X11 example/test of the libmigdb. + Run it from an X11 terminal (xterm, Eterm, etc.). + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +int wait_for_stop(MIDebugger &d) +{ + int res=1; + mi_stop *sr; + + while (!d.Poll(sr)) + usleep(1000); + /* The end of the async. */ + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + printf("mi_error: %d\nmi_error_from_gdb: %s\n",mi_error,mi_error_from_gdb); + res=0; + } + return res; +} + +int DoTryRun(int res, MIDebugger &d) +{ + if (!res) + { + printf("Error in executing!\n"); + return 0; + } + if (!wait_for_stop(d)) + return 0; + return 1; +} + +#define TryRun(a,b) if (!DoTryRun(a,b)) return 1 + +int main(int argc, char *argv[]) +{ + mi_bkpt *bk; + mi_wp *wp; + + // Debugging object, used as an auto var. + MIDebugger d; + + // You can use any gdb you want: + //MIDebugger::SetGDBExe("/usr/src/gdb-6.1.1/gdb/gdb"); + // You can use a terminal different than xterm: + //MIDebugger::SetXTerm("/usr/bin/Eterm"); + // You can specify commands for gdb + MIDebugger::SetGDBStartFile("cmds.txt"); + + // Connect to gdb child. + if (!d.Connect()) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + d.SetConsoleCB(cb_console); + d.SetTargetCB(cb_target); + d.SetLogCB(cb_log); + d.SetAsyncCB(cb_async); + d.SetToGDBCB(cb_to); + d.SetFromGDBCB(cb_from); + + // Set the name of the child and the command line aguments. + // It also opens the xterm. + if (!d.SelectTargetX11("./test_target","prb1 2 prb3")) + { + printf("Error setting exec y args\n"); + return 1; + } + + /* Set a breakpoint. */ + bk=d.Breakpoint("test_target.cc",15); + if (!bk) + { + printf("Error setting breakpoint\n"); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + + /* You can do things like: + gmi_break_delete(h,bk->number); + gmi_break_set_times(h,bk->number,2); + gmi_break_set_condition(h,bk->number,"1"); + gmi_break_state(h,bk->number,0);*/ + /* If we no longer need this data we can release it. */ + mi_free_bkpt(bk); + + /* Set a watchpoint, that's a data breakpoint. */ + wp=d.Watchpoint(wm_write,"v"); + if (!wp) + { + printf("Error al setting watchpoint\n"); + return 1; + } + printf("Watchpoint %d for expression: %s\n",wp->number,wp->exp); + mi_free_wp(wp); + + TryRun(d.StepOver(),d); + /* Run the program. */ + TryRun(d.RunOrContinue(),d); + /* Here we should be stopped at the breakpoint. */ + + /* Continue execution. */ + TryRun(d.RunOrContinue(),d); + /* Here we should be terminated. */ + + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + sleep(5); + + return 0; +} diff --git a/examples/x11_fr_test.c b/examples/x11_fr_test.c new file mode 100644 index 0000000..dd0f4f7 --- /dev/null +++ b/examples/x11_fr_test.c @@ -0,0 +1,329 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + X11 example/test of the libmigdb. + Run it from an X11 terminal (xterm, Eterm, etc.). + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +void print_frames(mi_frames *f, int free_f) +{ + mi_frames *ff=f; + + if (!f) + { + printf("Error! empty frames info\n"); + return; + } + while (f) + { + printf("Level %d, addr %p, func %s, where: %s:%d args? %c\n",f->level,f->addr, + f->func,f->file,f->line,f->args ? 'y' : 'n'); + f=f->next; + } + if (free_f) + mi_free_frames(ff); +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + print_frames(sr->frame,0); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + res=0; + } + return res; +} + +void print_gvar(mi_gvar *v) +{ + if (!v) + { + printf("Error! failed to define variable\n"); + return; + } + printf("Variable name: '%s', type: '%s', number of children: %d format: %s expression: %s lang: %s editable: %c\n", + v->name,v->type,v->numchild,mi_format_enum_to_str(v->format), + v->exp,mi_lang_enum_to_str(v->lang),v->attr & MI_ATTR_EDITABLE ? 'y' : 'n'); +} + +void print_update(mi_gvar_chg *changed) +{ + printf("List of changed variables:\n"); + while (changed) + { + printf("Name: %s\nIn scope: %c\n",changed->name,changed->in_scope ? 'y' : 'n'); + if (changed->in_scope && changed->new_type) + { + printf("New type: %s\nNew num children: %d\n",changed->new_type,changed->new_num_children); + } + changed=changed->next; + printf("\n"); + } +} + +void print_children(mi_gvar *ch) +{ + int i; + mi_gvar *s; + + if (!ch->child) + { + printf("Error! getting children list\n"); + return; + } + printf("\nChildren List (%d):\n",ch->numchild); + for (i=0, s=ch->child; inumchild; s++, i++) + { + printf("Name: %s Exp: %s Children: %d",s->name,s->exp,s->numchild); + if (s->type) + printf(" Type: %s",s->type); + if (s->value) + printf(" Value: %s",s->value); + printf("\n"); + } + printf("\n"); +} + + +int main(int argc, char *argv[]) +{ + mi_aux_term *xterm_tty=NULL; + mi_bkpt *bk; + mi_frames *fr; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + mi_gvar *gv, *gv2; + mi_gvar_chg *changed; + char *value; + int r_assign; + + /* You can use any gdb you want: */ + /*mi_set_gdb_exe("/usr/src/gdb-6.1.1/gdb/gdb");*/ + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./target_frames","")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Open an xterm to be used as terminal for the debuggy. */ + xterm_tty=gmi_start_xterm(); + if (!xterm_tty) + printf("Error opening auxiliar terminal, we'll use current one.\n"); + else + printf("XTerm opened @ %s\n",xterm_tty->tty); + + /* Tell gdb to attach the child to a terminal. */ + if (!gmi_target_terminal(h,xterm_tty ? xterm_tty->tty : ttyname(STDIN_FILENO))) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Set a breakpoint. */ + bk=gmi_break_insert(h,"target_frames.cc",7); + if (!bk) + { + printf("Error setting breakpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + + /* If we no longer need this data we can release it. */ + mi_free_bkpt(bk); + + /* Run the program. */ + if (!gmi_exec_run(h)) + { + printf("Error in run!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be stopped at the breakpoint. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Get information about the calling stack. */ + fr=gmi_stack_list_frames(h); + printf("\nCalling stack:\n\n"); + print_frames(fr,1); + printf("\n"); + fr=gmi_stack_info_frame(h); + printf("\nCurrent frame:\n\n"); + print_frames(fr,1); + printf("\n"); + printf("Stack depth: %d\n",gmi_stack_info_depth_get(h)); + gmi_stack_select_frame(h,1); + fr=gmi_stack_info_frame(h); + printf("\nFrame 1:\n\n"); + print_frames(fr,1); + printf("\n"); + + if (0) + { + gv=gmi_var_create(h,-1,"v"); + print_gvar(gv); + gmi_var_show_format(h,gv); + print_gvar(gv); + gmi_var_info_num_children(h,gv); + print_gvar(gv); + gmi_var_info_type(h,gv); + print_gvar(gv); + gmi_var_info_expression(h,gv); + print_gvar(gv); + gmi_var_show_attributes(h,gv); + print_gvar(gv); + } + else + { + gv=gmi_full_var_create(h,-1,"v"); + print_gvar(gv); + } + gv2=gmi_full_var_create(h,-1,"sup"); + print_gvar(gv2); + gmi_var_set_format(h,gv,fm_hexadecimal); + print_gvar(gv); + + /* Continue execution. */ + if (!gmi_exec_until(h,"target_frames.cc",21)) + { + printf("Error in exec until!\n"); + mi_disconnect(h); + return 1; + } + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + gmi_var_update(h,NULL,&changed); + print_update(changed); + mi_free_gvar_chg(changed); + + r_assign=gmi_var_assign(h,gv,"i+5"); + if (r_assign && gv->value) + printf("\nAssigned v=%s\n\n",gv->value); + + gmi_var_list_children(h,gv2); + print_children(gv2); + + gmi_var_evaluate_expression(h,gv2); + printf("\n%s = %s\n\n",gv2->exp,gv2->value); + + value=gmi_data_evaluate_expression(h,gv2->exp); + printf("\n%s = %s\n\n",gv2->exp,value); + free(value); + + gmi_var_delete(h,gv); + mi_free_gvar(gv); + gmi_var_delete(h,gv2); + mi_free_gvar(gv2); + + /* Continue execution. */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be terminated. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + sleep(5); + gmi_end_aux_term(xterm_tty); + + return 0; +} diff --git a/examples/x11_test.c b/examples/x11_test.c new file mode 100644 index 0000000..4f2be25 --- /dev/null +++ b/examples/x11_test.c @@ -0,0 +1,192 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + X11 example/test of the libmigdb. + Run it from an X11 terminal (xterm, Eterm, etc.). + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + printf("mi_error: %d\nmi_error_from_gdb: %s\n",mi_error,mi_error_from_gdb); + res=0; + } + return res; +} + +int main(int argc, char *argv[]) +{ + mi_aux_term *xterm_tty=NULL; + mi_bkpt *bk; + mi_wp *wp; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + + /* You can use any gdb you want: */ + /*mi_set_gdb_exe("/usr/src/gdb-6.1.1/gdb/gdb");*/ + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./test_target","prb1 2 prb3")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Open an xterm to be used as terminal for the debuggy. */ + xterm_tty=gmi_start_xterm(); + if (!xterm_tty) + printf("Error opening auxiliar terminal, we'll use current one.\n"); + else + printf("XTerm opened @ %s\n",xterm_tty->tty); + + /* Tell gdb to attach the child to a terminal. */ + if (!gmi_target_terminal(h,xterm_tty ? xterm_tty->tty : ttyname(STDIN_FILENO))) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Set a breakpoint. */ + bk=gmi_break_insert(h,"test_target.cc",12); + if (!bk) + { + printf("Error setting breakpoint\n"); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + + /* You can do things like: + gmi_break_delete(h,bk->number); + gmi_break_set_times(h,bk->number,2); + gmi_break_set_condition(h,bk->number,"1"); + gmi_break_state(h,bk->number,0);*/ + /* If we no longer need this data we can release it. */ + mi_free_bkpt(bk); + + /* Set a watchpoint, that's a data breakpoint. */ + wp=gmi_break_watch(h,wm_write,"v"); + if (!wp) + { + printf("Error al setting watchpoint\n"); + return 1; + } + printf("Watchpoint %d for expression: %s\n",wp->number,wp->exp); + mi_free_wp(wp); + + /* Run the program. */ + if (!gmi_exec_run(h)) + { + printf("Error in run!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be stopped at the breakpoint. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Continue execution. */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be terminated. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + sleep(5); + gmi_end_aux_term(xterm_tty); + + return 0; +} diff --git a/examples/x11_wp_test.c b/examples/x11_wp_test.c new file mode 100644 index 0000000..39bdebf --- /dev/null +++ b/examples/x11_wp_test.c @@ -0,0 +1,292 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + X11 example/test of the libmigdb. + Run it from an X11 terminal (xterm, Eterm, etc.). + +***************************************************************************/ + +#include +#include //usleep +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +void print_frames(mi_frames *f) +{ + if (!f) + { + printf("Error! empty frames info\n"); + return; + } + while (f) + { + printf("Level %d, addr %p, func %s, where: %s:%d args? %c\n",f->level,f->addr, + f->func,f->file,f->line,f->args ? 'y' : 'n'); + f=f->next; + } + mi_free_frames(f); +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + print_frames(sr->frame); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + printf("mi_error: %s (%d)\nmi_error_from_gdb: %s\n",mi_get_error_str(), + mi_error,mi_error_from_gdb); + res=0; + } + return res; +} + + +void print_gvar(mi_gvar *v) +{ + if (!v) + { + printf("Error! failed to define variable\n"); + return; + } + printf("Variable name: '%s', type: '%s', number of children: %d format: %s expression: %s lang: %s editable: %c\n", + v->name,v->type,v->numchild,mi_format_enum_to_str(v->format), + v->exp,mi_lang_enum_to_str(v->lang),v->attr & MI_ATTR_EDITABLE ? 'y' : 'n'); +} + +void print_update(mi_gvar_chg *changed) +{ + printf("List of changed variables:\n"); + while (changed) + { + printf("Name: %s\nIn scope: %c\n",changed->name,changed->in_scope ? 'y' : 'n'); + if (changed->in_scope && changed->new_type) + { + printf("New type: %s\nNew num children: %d\n",changed->new_type,changed->new_num_children); + } + changed=changed->next; + printf("\n"); + } +} + +void print_children(mi_gvar *ch) +{ + int i; + mi_gvar *s; + + if (!ch->child) + { + printf("Error! getting children list\n"); + return; + } + printf("\nChildren List (%d):\n",ch->numchild); + for (i=0, s=ch->child; inumchild; s++, i++) + { + printf("Name: %s Exp: %s Children: %d",s->name,s->exp,s->numchild); + if (s->type) + printf(" Type: %s",s->type); + if (s->value) + printf(" Value: %s",s->value); + printf("\n"); + } + printf("\n"); +} + +enum cont_mode { cm_run, cm_continue, cm_finish, cm_step }; + +void do_continue(mi_h *h, enum cont_mode mode) +{ + int res; + switch (mode) + { + case cm_run: + res=gmi_exec_run(h); + break; + case cm_continue: + res=gmi_exec_continue(h); + break; + case cm_finish: + res=gmi_exec_finish(h); + break; + case cm_step: + res=gmi_exec_step(h); + break; + } + /* Continue the program. */ + if (!res) + { + printf("Error in run!\n"); + mi_disconnect(h); + exit(1); + } + /* Here we should be stopped at the breakpoint. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + exit(1); + } +} + + +int main(int argc, char *argv[]) +{ + mi_aux_term *xterm_tty=NULL; + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + mi_bkpt *bk; + mi_wp *wp, *wp2; + + /* You can use any gdb you want: */ + /*mi_set_gdb_exe("/usr/src/gdb-6.1.1/gdb/gdb");*/ + /* You can use a terminal different than xterm: */ + /*mi_set_xterm_exe("/usr/bin/Eterm");*/ + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Open an xterm to be used as terminal for the debuggy. */ + xterm_tty=gmi_start_xterm(); + if (!xterm_tty) + printf("Error opening auxiliar terminal, we'll use current one.\n"); + else + printf("XTerm opened @ %s\n",xterm_tty->tty); + + /* Tell gdb to attach the child to a terminal. */ + if (!gmi_target_terminal(h,xterm_tty ? xterm_tty->tty : ttyname(STDIN_FILENO))) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"./target_frames","")) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Break on FuncLev1. */ + bk=gmi_break_insert_full(h,1,0,NULL,-1,-1,"FuncLev1"); + if (!bk) + { + printf("Error setting breakpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Breakpoint %d @ function: %s\n",bk->number,bk->func); + mi_free_bkpt(bk); + + /* Set a watchpoint, that's a data breakpoint. */ + wp=gmi_break_watch(h,wm_write,"v"); + if (!wp) + { + printf("Error al setting watchpoint\n"); + mi_disconnect(h); + return 1; + } + printf("Watchpoint %d for expression: %s\n",wp->number,wp->exp); + mi_free_wp(wp); + + /* Run the program. */ + do_continue(h,cm_run); + + wp2=gmi_break_watch(h,wm_write,"i"); + mi_free_wp(wp2); + + if (1) + { + do_continue(h,cm_finish); + do_continue(h,cm_finish); + do_continue(h,cm_finish); + do_continue(h,cm_continue); + do_continue(h,cm_continue); + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + } + else + { + do_continue(h,cm_step); + do_continue(h,cm_step); + do_continue(h,cm_step); + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + //sleep(5); + gmi_end_aux_term(xterm_tty); + + return 0; +} diff --git a/files b/files new file mode 100644 index 0000000..fd8cf34 --- /dev/null +++ b/files @@ -0,0 +1,16 @@ +libmigdb/doc/*.frt +libmigdb/doc/*.html +libmigdb/examples/Makefile +libmigdb/examples/*.c* +libmigdb/examples/cmds.txt +libmigdb/src/Makefile +libmigdb/src/*.c* +libmigdb/src/*.h +libmigdb/files +libmigdb/DJGPP.why +libmigdb/GPL-license +libmigdb/Makefile +libmigdb/README +libmigdb/compress.sh +libmigdb/version.c +libmigdb/change.log diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..a2bee77 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,3 @@ +*.dst +.*.dst +*.epr* diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..b94d7de --- /dev/null +++ b/src/Makefile @@ -0,0 +1,55 @@ +#!/usr/bin/make +PREFIX=/usr + +all: libmigdb.a + +CFLAGS=-O2 -Wall -gstabs+3 -I. +CXXFLAGS=-O2 -Wall -gstabs+3 +LDLIBS= + +connect.o: mi_gdb.h + +parse.o: mi_gdb.h + +cpp_int.o: mi_gdb.h + +prg_control.o: mi_gdb.h + +misc.o: mi_gdb.h + +breakpoint.o: mi_gdb.h + +target_man.o: mi_gdb.h + +get_free_vt.o: mi_gdb.h + +get_free_pty.o: mi_gdb.h + +data_man.o: mi_gdb.h + +stack_man.o: mi_gdb.h + +symbol_query.o: mi_gdb.h + +thread.o: mi_gdb.h + +var_obj.o: mi_gdb.h + +alloc.o: mi_gdb.h + +error.o: mi_gdb.h + +libmigdb.a: connect.o parse.o prg_control.o misc.o breakpoint.o target_man.o \ + get_free_vt.o get_free_pty.o data_man.o stack_man.o symbol_query.o \ + thread.o var_obj.o alloc.o error.o cpp_int.o + ar rcs $@ $^ + +clean: + -@rm *.o *.a 2> /dev/null + +install: + install -d -m 755 $(PREFIX)/lib + install -m 644 libmigdb.a $(PREFIX)/lib + install -d -m 755 $(PREFIX)/include + install -m 644 mi_gdb.h $(PREFIX)/include + diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..9e1ffa7 --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,308 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Allocator. + Comments: + Most alloc/free routines are here. Free routines must accept NULL +pointers. Alloc functions must set mi_error.@p + +***************************************************************************/ + +#include "mi_gdb.h" + +void *mi_calloc(size_t count, size_t sz) +{ + void *res=calloc(count,sz); + if (!res) + mi_error=MI_OUT_OF_MEMORY; + return res; +} + +void *mi_calloc1(size_t sz) +{ + return mi_calloc(1,sz); +} + +char *mi_malloc(size_t sz) +{ + char *res=malloc(sz); + if (!res) + mi_error=MI_OUT_OF_MEMORY; + return res; +} + +mi_results *mi_alloc_results(void) +{ + return (mi_results *)mi_calloc1(sizeof(mi_results)); +} + +mi_output *mi_alloc_output(void) +{ + return (mi_output *)mi_calloc1(sizeof(mi_output)); +} + +mi_frames *mi_alloc_frames(void) +{ + return (mi_frames *)mi_calloc1(sizeof(mi_frames)); +} + +mi_gvar *mi_alloc_gvar(void) +{ + return (mi_gvar *)mi_calloc1(sizeof(mi_gvar)); +} + +mi_gvar_chg *mi_alloc_gvar_chg(void) +{ + return (mi_gvar_chg *)mi_calloc1(sizeof(mi_gvar_chg)); +} + +mi_bkpt *mi_alloc_bkpt(void) +{ + mi_bkpt *b=(mi_bkpt *)mi_calloc1(sizeof(mi_bkpt)); + if (b) + { + b->thread=-1; + b->ignore=-1; + } + return b; +} + +mi_wp *mi_alloc_wp(void) +{ + return (mi_wp *)mi_calloc1(sizeof(mi_wp)); +} + +mi_stop *mi_alloc_stop(void) +{ + return (mi_stop *)mi_calloc1(sizeof(mi_stop)); +} + +mi_asm_insns *mi_alloc_asm_insns(void) +{ + return (mi_asm_insns *)mi_calloc1(sizeof(mi_asm_insns)); +} + +mi_asm_insn *mi_alloc_asm_insn(void) +{ + return (mi_asm_insn *)mi_calloc1(sizeof(mi_asm_insn)); +} + +mi_chg_reg *mi_alloc_chg_reg(void) +{ + return (mi_chg_reg *)mi_calloc1(sizeof(mi_chg_reg)); +} + +/***************************************************************************** + Free functions +*****************************************************************************/ + +void mi_free_frames(mi_frames *f) +{ + mi_frames *aux; + + while (f) + { + free(f->func); + free(f->file); + free(f->from); + mi_free_results(f->args); + aux=f->next; + free(f); + f=aux; + } +} + +void mi_free_bkpt(mi_bkpt *b) +{ + mi_bkpt *aux; + + while (b) + { + free(b->func); + free(b->file); + free(b->file_abs); + free(b->cond); + aux=b->next; + free(b); + b=aux; + } +} + +void mi_free_gvar(mi_gvar *v) +{ + mi_gvar *aux; + + while (v) + { + free(v->name); + free(v->type); + free(v->exp); + free(v->value); + if (v->numchild && v->child) + mi_free_gvar(v->child); + aux=v->next; + free(v); + v=aux; + } +} + +void mi_free_gvar_chg(mi_gvar_chg *p) +{ + mi_gvar_chg *aux; + + while (p) + { + free(p->name); + free(p->new_type); + aux=p->next; + free(p); + p=aux; + } +} + +void mi_free_results_but(mi_results *r, mi_results *no) +{ + mi_results *aux; + + while (r) + { + if (r==no) + { + aux=r->next; + r->next=NULL; + r=aux; + } + else + { + free(r->var); + switch (r->type) + { + case t_const: + free(r->v.cstr); + break; + case t_tuple: + case t_list: + mi_free_results_but(r->v.rs,no); + break; + } + aux=r->next; + free(r); + r=aux; + } + } +} + +void mi_free_results(mi_results *r) +{ + mi_free_results_but(r,NULL); +} + +void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r) +{ + mi_output *aux; + + while (r) + { + if (r==no) + { + aux=r->next; + r->next=NULL; + r=aux; + } + else + { + if (r->c) + mi_free_results_but(r->c,no_r); + aux=r->next; + free(r); + r=aux; + } + } +} + +void mi_free_output(mi_output *r) +{ + mi_free_output_but(r,NULL,NULL); +} + +void mi_free_stop(mi_stop *s) +{ + if (!s) + return; + mi_free_frames(s->frame); + mi_free_wp(s->wp); + free(s->wp_old); + free(s->wp_val); + free(s->gdb_result_var); + free(s->return_value); + free(s->signal_name); + free(s->signal_meaning); + free(s); +} + +void mi_free_wp(mi_wp *wp) +{ + mi_wp *aux; + while (wp) + { + free(wp->exp); + aux=wp->next; + free(wp); + wp=aux; + } +} + +void mi_free_asm_insns(mi_asm_insns *i) +{ + mi_asm_insns *aux; + + while (i) + { + free(i->file); + mi_free_asm_insn(i->ins); + aux=i->next; + free(i); + i=aux; + } +} + +void mi_free_asm_insn(mi_asm_insn *i) +{ + mi_asm_insn *aux; + + while (i) + { + free(i->func); + free(i->inst); + aux=i->next; + free(i); + i=aux; + } +} + +/*void mi_free_charp_list(char **l) +{ + char **c=l; + while (c) + { + free(*c); + c++; + } + free(l); +}*/ + +void mi_free_chg_reg(mi_chg_reg *r) +{ + mi_chg_reg *aux; + while (r) + { + free(r->val); + free(r->name); + aux=r->next; + free(r); + r=aux; + } +} + diff --git a/src/breakpoint.c b/src/breakpoint.c new file mode 100644 index 0000000..645c274 --- /dev/null +++ b/src/breakpoint.c @@ -0,0 +1,265 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Breakpoint table commands. + Comments: + GDB/MI commands for the "Breakpoint Table Commands" section.@p + +@

    +gdb command:          Implemented?
    +
    +-break-after          Yes
    +-break-condition      Yes
    +-break-delete         Yes
    +-break-disable        Yes
    +-break-enable         Yes
    +-break-info           N.A. (info break NUMBER) (*)
    +-break-insert         Yes
    +-break-list           No (*)
    +-break-watch          Yes
    +@
    + +(*) I think the program should keep track of the breakpoints, so it will +be implemented when I have more time.@p + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_break_insert_fl(mi_h *h, const char *file, int line) +{ + mi_send(h,"-break-insert %s:%d\n",file,line); +} + +void mi_break_insert(mi_h *h, int temporary, int hard_assist, + const char *cond, int count, int thread, + const char *where) +{ + char s_count[32]; + char s_thread[32]; + + if (count>=0) + snprintf(s_count,32,"%d",count); + if (thread>=0) + snprintf(s_thread,32,"%d",thread); + if (cond) + // Conditions may contain spaces, in fact, if they don't gdb will add + // them after parsing. Enclosing the expression with "" solves the + // problem. + mi_send(h,"-break-insert %s %s -c \"%s\" %s %s %s %s %s\n", + temporary ? "-t" : "", + hard_assist ? "-h" : "", + cond, + count>=0 ? "-i" : "", count>=0 ? s_count : "", + thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", + where); + else + mi_send(h,"-break-insert %s %s %s %s %s %s %s\n", + temporary ? "-t" : "", + hard_assist ? "-h" : "", + count>=0 ? "-i" : "", count>=0 ? s_count : "", + thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", + where); +} + +void mi_break_insert_flf(mi_h *h, const char *file, int line, int temporary, + int hard_assist, const char *cond, int count, + int thread) +{ + char s_count[32]; + char s_thread[32]; + + if (count>=0) + snprintf(s_count,32,"%d",count); + if (thread>=0) + snprintf(s_thread,32,"%d",thread); + mi_send(h,"-break-insert %s %s %s %s %s %s %s %s %s:%d\n", + temporary ? "-t" : "", + hard_assist ? "-h" : "", + cond ? "-c" : "", cond ? cond : "", + count>=0 ? "-i" : "", count>=0 ? s_count : "", + thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", + file,line); +} + +void mi_break_delete(mi_h *h, int number) +{ + mi_send(h,"-break-delete %d\n",number); +} + +void mi_break_after(mi_h *h, int number, int count) +{ + mi_send(h,"-break-after %d %d\n",number,count); +} + +void mi_break_condition(mi_h *h, int number, const char *condition) +{ + mi_send(h,"-break-condition %d %s\n",number,condition); +} + +void mi_break_enable(mi_h *h, int number) +{ + mi_send(h,"-break-enable %d\n",number); +} + +void mi_break_disable(mi_h *h, int number) +{ + mi_send(h,"-break-disable %d\n",number); +} + +void mi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp) +{ + if (mode==wm_write) + mi_send(h,"-break-watch \"%s\"\n",exp); + else + mi_send(h,"-break-watch -%c \"%s\"\n",mode==wm_rw ? 'a' : 'r',exp); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Insert a breakpoint at file:line. + + Command: -break-insert file:line + Return: A new mi_bkpt structure with info about the breakpoint. NULL on +error. + +***************************************************************************/ + +mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line) +{ + mi_break_insert_fl(h,file,line); + return mi_res_bkpt(h); +} + +/**[txh]******************************************************************** + + Description: + Insert a breakpoint, all available options. + + Command: -break-insert + Return: A new mi_bkpt structure with info about the breakpoint. NULL on +error. + +***************************************************************************/ + +mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, + const char *cond, int count, int thread, + const char *where) +{ + mi_break_insert(h,temporary,hard_assist,cond,count,thread,where); + return mi_res_bkpt(h); +} + +/**[txh]******************************************************************** + + Description: + Insert a breakpoint, all available options. + + Command: -break-insert [ops] file:line + Return: A new mi_bkpt structure with info about the breakpoint. NULL on +error. + +***************************************************************************/ + +mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line, + int temporary, int hard_assist, + const char *cond, int count, int thread) +{ + mi_break_insert_flf(h,file,line,temporary,hard_assist,cond,count,thread); + return mi_res_bkpt(h); +} + +/**[txh]******************************************************************** + + Description: + Remove a breakpoint. + + Command: -break-delete + Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the +console. + +***************************************************************************/ + +int gmi_break_delete(mi_h *h, int number) +{ + mi_break_delete(h,number); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Modify the "ignore" count for a breakpoint. + + Command: -break-after + Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the +console. + +***************************************************************************/ + +int gmi_break_set_times(mi_h *h, int number, int count) +{ + mi_break_after(h,number,count); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Associate a condition with the breakpoint. + + Command: -break-condition + Return: !=0 OK + +***************************************************************************/ + +int gmi_break_set_condition(mi_h *h, int number, const char *condition) +{ + mi_break_condition(h,number,condition); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Enable or disable a breakpoint. + + Command: -break-enable + -break-disable + Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the +console. + +***************************************************************************/ + +int gmi_break_state(mi_h *h, int number, int enable) +{ + if (enable) + mi_break_enable(h,number); + else + mi_break_disable(h,number); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Set a watchpoint. It doesn't work for remote targets! + + Command: -break-watch + Return: A new mi_wp structure with info about the watchpoint. NULL on +error. + +***************************************************************************/ + +mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp) +{ + mi_break_watch(h,mode,exp); + return mi_res_wp(h); +} + diff --git a/src/connect.c b/src/connect.c new file mode 100644 index 0000000..319be6f --- /dev/null +++ b/src/connect.c @@ -0,0 +1,887 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2009 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Connect. + Comments: + This module handles the dialog with gdb, including starting and stopping +gdb.@p + +GDB Bug workaround for "file -readnow": I tried to workaround a bug using +it but looks like this option also have bugs!!!! so I have to use the +command line option --readnow. +It also have a bug!!!! when the binary is changed and gdb must reload it +this option is ignored. So it looks like we have no solution but 3 gdb bugs +in a row. + +***************************************************************************/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mi_gdb.h" + +#ifndef TEMP_FAILURE_RETRY + #define TEMP_FAILURE_RETRY(a) (a) +#endif + +int mi_error=MI_OK; +char *mi_error_from_gdb=NULL; +static char *gdb_exe=NULL; +static char *xterm_exe=NULL; +static char *gdb_start=NULL; +static char *gdb_conn=NULL; +static char *main_func=NULL; +static char disable_psym_search_workaround=0; + +mi_h *mi_alloc_h() +{ + mi_h *h=(mi_h *)calloc(1,sizeof(mi_h)); + if (!h) + { + mi_error=MI_OUT_OF_MEMORY; + return NULL; + } + h->to_gdb[0]=h->to_gdb[1]=h->from_gdb[0]=h->from_gdb[1]=-1; + h->pid=-1; + return h; +} + +int mi_check_running_pid(pid_t pid) +{ + int status; + + if (pid<=0) + return 0; + /* If waitpid returns the number of our child means it communicated + to as a termination status. */ + if (waitpid(pid,&status,WNOHANG)==pid) + { + pid=0; + return 0; + } + return 1; +} + +int mi_check_running(mi_h *h) +{ + return !h->died && mi_check_running_pid(h->pid); +} + +void mi_kill_child(pid_t pid) +{ + kill(pid,SIGTERM); + usleep(100000); + if (mi_check_running_pid(pid)) + { + int status; + kill(pid,SIGKILL); + waitpid(pid,&status,0); + } +} + +void mi_free_h(mi_h **handle) +{ + mi_h *h=*handle; + if (h->to_gdb[0]>=0) + close(h->to_gdb[0]); + if (h->to) + fclose(h->to); + else if (h->to_gdb[1]>=0) + close(h->to_gdb[1]); + if (h->from) + fclose(h->from); + else if (h->from_gdb[0]>=0) + close(h->from_gdb[0]); + if (h->from_gdb[1]>=0) + close(h->from_gdb[1]); + if (mi_check_running(h)) + {/* GDB is running! */ + mi_kill_child(h->pid); + } + if (h->line) + free(h->line); + mi_free_output(h->po); + free(h->catched_console); + free(h); + *handle=NULL; +} + +void mi_set_nonblk(int h) +{ + int flf; + flf=fcntl(h,F_GETFL,0); + flf=flf | O_NONBLOCK; + fcntl(h,F_SETFL,flf); +} + +int mi_getline(mi_h *h) +{ + char c; + + while (read(h->from_gdb[0],&c,1)==1) + { + if (h->lread>=h->llen) + { + h->llen=h->lread+128; + h->line=(char *)realloc(h->line,h->llen); + if (!h->line) + { + h->llen=0; + h->lread=0; + return -1; + } + } + if (c=='\n') + { + int ret=h->lread; + h->line[ret]=0; + h->lread=0; + return ret; + } + if (c!='\r') + { + h->line[h->lread]=c; + h->lread++; + } + } + return 0; +} + +char *get_cstr(mi_output *o) +{ + if (!o->c || o->c->type!=t_const) + return NULL; + return o->c->v.cstr; +} + +int mi_get_response(mi_h *h) +{ + int l=mi_getline(h); + if (!l) + return 0; + + if (h->from_gdb_echo) + h->from_gdb_echo(h->line,h->from_gdb_echo_data); + if (strncmp(h->line,"(gdb)",5)==0) + {/* End of response. */ + return 1; + } + else + {/* Add to the response. */ + mi_output *o; + int add=1, is_exit=0; + o=mi_parse_gdb_output(h->line); + + if (!o) + return 0; + /* Tunneled streams callbacks. */ + if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_STREAM) + { + char *aux; + add=0; + switch (o->sstype) + { + case MI_SST_CONSOLE: + aux=get_cstr(o); + if (h->console) + h->console(aux,h->console_data); + if (h->catch_console && aux) + { + h->catch_console--; + if (!h->catch_console) + { + free(h->catched_console); + h->catched_console=strdup(aux); + } + } + break; + case MI_SST_TARGET: + /* This one seems to be useless. */ + if (h->target) + h->target(get_cstr(o),h->target_data); + break; + case MI_SST_LOG: + if (h->log) + h->log(get_cstr(o),h->log_data); + break; + } + } + else if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_ASYNC) + { + if (h->async) + h->async(o,h->async_data); + } + else if (o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_ERROR) + {/* Error from gdb, record it. */ + mi_error=MI_FROM_GDB; + free(mi_error_from_gdb); + mi_error_from_gdb=NULL; + if (o->c && strcmp(o->c->var,"msg")==0 && o->c->type==t_const) + mi_error_from_gdb=strdup(o->c->v.cstr); + } + is_exit=(o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_EXIT); + /* Add to the list of responses. */ + if (add) + { + if (h->last) + h->last->next=o; + else + h->po=o; + h->last=o; + } + else + mi_free_output(o); + /* Exit RR means gdb exited, we won't get a new prompt ;-) */ + if (is_exit) + return 1; + } + + return 0; +} + +mi_output *mi_retire_response(mi_h *h) +{ + mi_output *ret=h->po; + h->po=h->last=NULL; + return ret; +} + +mi_output *mi_get_response_blk(mi_h *h) +{ + int r; + /* Sometimes gdb dies. */ + if (!mi_check_running(h)) + { + h->died=1; + mi_error=MI_GDB_DIED; + return NULL; + } + do + { + if (1) + { + /* + That's a must. If we just keep trying to read and failing things + become really sloooowwww. Instead we try and if it fails we wait + until something is available. + TODO: Implement something with the time out, a callback to ask the + application is we have to wait or not could be a good thing. + */ + fd_set set; + struct timeval timeout; + int ret; + + r=mi_get_response(h); + if (r) + return mi_retire_response(h); + + FD_ZERO(&set); + FD_SET(h->from_gdb[0],&set); + timeout.tv_sec=h->time_out; + timeout.tv_usec=0; + ret=TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,&timeout)); + if (!ret) + { + if (!mi_check_running(h)) + { + h->died=1; + mi_error=MI_GDB_DIED; + return NULL; + } + if (h->time_out_cb) + ret=h->time_out_cb(h->time_out_cb_data); + if (!ret) + { + mi_error=MI_GDB_TIME_OUT; + return NULL; + } + } + } + else + { + r=mi_get_response(h); + if (r) + return mi_retire_response(h); + else + usleep(100); + } + } + while (!r); + + return NULL; +} + +void mi_send_commands(mi_h *h, const char *file) +{ + FILE *f; + char b[PATH_MAX]; + + //printf("File: %s\n",file); + if (!file) + return; + f=fopen(file,"rt"); + if (!f) + return; + while (!feof(f)) + { + if (fgets(b,PATH_MAX,f)) + { + //printf("Send: %s\n",b); + mi_send(h,b); + mi_res_simple_done(h); + } + } + fclose(f); +} + +void mi_send_target_commands(mi_h *h) +{ + mi_send_commands(h,gdb_conn); +} + +/**[txh]******************************************************************** + + Description: + Connect to a local copy of gdb. Note that the mi_h structure is something +similar to a "FILE *" for stdio. + + Return: A new mi_h structure or NULL on error. + +***************************************************************************/ + +mi_h *mi_connect_local() +{ + mi_h *h; + const char *gdb=mi_get_gdb_exe(); + + /* Start without error. */ + mi_error=MI_OK; + /* Verify we have a GDB binary. */ + if (access(gdb,X_OK)) + { + mi_error=MI_MISSING_GDB; + return NULL; + } + /* Alloc the handle structure. */ + h=mi_alloc_h(); + if (!h) + return h; + h->time_out=MI_DEFAULT_TIME_OUT; + /* Create the pipes to connect with the child. */ + if (pipe(h->to_gdb) || pipe(h->from_gdb)) + { + mi_error=MI_PIPE_CREATE; + mi_free_h(&h); + return NULL; + } + mi_set_nonblk(h->to_gdb[1]); + mi_set_nonblk(h->from_gdb[0]); + /* Associate streams to the file handles. */ + h->to=fdopen(h->to_gdb[1],"w"); + h->from=fdopen(h->from_gdb[0],"r"); + if (!h->to || !h->from) + { + mi_error=MI_PIPE_CREATE; + mi_free_h(&h); + return NULL; + } + /* Create the child. */ + h->pid=fork(); + if (h->pid==0) + {/* We are the child. */ + char *argv[5]; + /* Connect stdin/out to the pipes. */ + dup2(h->to_gdb[0],STDIN_FILENO); + dup2(h->from_gdb[1],STDOUT_FILENO); + /* Pass the control to gdb. */ + argv[0]=(char *)gdb; /* Is that OK? */ + argv[1]="--interpreter=mi"; + argv[2]="--quiet"; + argv[3]=disable_psym_search_workaround ? 0 : "--readnow"; + argv[4]=0; + execvp(argv[0],argv); + /* We get here only if exec failed. */ + _exit(127); + } + /* We are the parent. */ + if (h->pid==-1) + {/* Fork failed. */ + mi_error=MI_FORK; + mi_free_h(&h); + return NULL; + } + if (!mi_check_running(h)) + { + mi_error=MI_DEBUGGER_RUN; + mi_free_h(&h); + return NULL; + } + /* Wait for the prompt. */ + mi_get_response_blk(h); + /* Send the start-up commands */ + mi_send_commands(h,gdb_start); + + return h; +} + +/**[txh]******************************************************************** + + Description: + Close connection. You should ask gdb to quit first @x{gmi_gdb_exit}. + +***************************************************************************/ + +void mi_disconnect(mi_h *h) +{ + mi_free_h(&h); + free(mi_error_from_gdb); + mi_error_from_gdb=NULL; +} + +void mi_set_console_cb(mi_h *h, stream_cb cb, void *data) +{ + h->console=cb; + h->console_data=data; +} + +void mi_set_target_cb(mi_h *h, stream_cb cb, void *data) +{ + h->target=cb; + h->target_data=data; +} + +void mi_set_log_cb(mi_h *h, stream_cb cb, void *data) +{ + h->log=cb; + h->log_data=data; +} + +stream_cb mi_get_console_cb(mi_h *h, void **data) +{ + if (data) + *data=h->console_data; + return h->console; +} + +stream_cb mi_get_target_cb(mi_h *h, void **data) +{ + if (data) + *data=h->target_data; + return h->target; +} + +stream_cb mi_get_log_cb(mi_h *h, void **data) +{ + if (data) + *data=h->log_data; + return h->log; +} + +void mi_set_async_cb(mi_h *h, async_cb cb, void *data) +{ + h->async=cb; + h->async_data=data; +} + +async_cb mi_get_async_cb(mi_h *h, void **data) +{ + if (data) + *data=h->async_data; + return h->async; +} + +void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data) +{ + h->to_gdb_echo=cb; + h->to_gdb_echo_data=data; +} + +void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data) +{ + h->from_gdb_echo=cb; + h->from_gdb_echo_data=data; +} + +stream_cb mi_get_to_gdb_cb(mi_h *h, void **data) +{ + if (data) + *data=h->to_gdb_echo_data; + return h->to_gdb_echo; +} + +stream_cb mi_get_from_gdb_cb(mi_h *h, void **data) +{ + if (data) + *data=h->from_gdb_echo_data; + return h->from_gdb_echo; +} + +void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data) +{ + h->time_out_cb=cb; + h->time_out_cb_data=data; +} + +tm_cb mi_get_time_out_cb(mi_h *h, void **data) +{ + if (data) + *data=h->time_out_cb_data; + return h->time_out_cb; +} + +void mi_set_time_out(mi_h *h, int to) +{ + h->time_out=to; +} + +int mi_get_time_out(mi_h *h) +{ + return h->time_out; +} + +int mi_send(mi_h *h, const char *format, ...) +{ + int ret; + char *str; + va_list argptr; + + if (h->died) + return 0; + + va_start(argptr,format); + ret=vasprintf(&str,format,argptr); + va_end(argptr); + fputs(str,h->to); + fflush(h->to); + if (h->to_gdb_echo) + h->to_gdb_echo(str,h->to_gdb_echo_data); + free(str); + + return ret; +} + +void mi_clean_up_globals() +{ + free(gdb_exe); + gdb_exe=NULL; + free(xterm_exe); + xterm_exe=NULL; + free(gdb_start); + gdb_start=NULL; + free(gdb_conn); + gdb_conn=NULL; + free(main_func); + main_func=NULL; +} + +void mi_register_exit() +{ + static int registered=0; + if (!registered) + { + registered=1; + atexit(mi_clean_up_globals); + } +} + +void mi_set_gdb_exe(const char *name) +{ + free(gdb_exe); + gdb_exe=name ? strdup(name) : NULL; + mi_register_exit(); +} + +void mi_set_gdb_start(const char *name) +{ + free(gdb_start); + gdb_start=name ? strdup(name) : NULL; + mi_register_exit(); +} + +void mi_set_gdb_conn(const char *name) +{ + free(gdb_conn); + gdb_conn=name ? strdup(name) : NULL; + mi_register_exit(); +} + +static +char *mi_search_in_path(const char *file) +{ + char *path, *pt, *r; + char test[PATH_MAX]; + struct stat st; + + path=getenv("PATH"); + if (!path) + return NULL; + pt=strdup(path); + r=strtok(pt,":"); + while (r) + { + strcpy(test,r); + strcat(test,"/"); + strcat(test,file); + if (stat(test,&st)==0 && S_ISREG(st.st_mode)) + { + free(pt); + return strdup(test); + } + r=strtok(NULL,":"); + } + free(pt); + return NULL; +} + +const char *mi_get_gdb_exe() +{ + if (!gdb_exe) + {/* Look for gdb in path */ + gdb_exe=mi_search_in_path("gdb"); + if (!gdb_exe) + return "/usr/bin/gdb"; + } + return gdb_exe; +} + +const char *mi_get_gdb_start() +{ + return gdb_start; +} + +const char *mi_get_gdb_conn() +{ + return gdb_conn; +} + +void mi_set_xterm_exe(const char *name) +{ + free(xterm_exe); + xterm_exe=name ? strdup(name) : NULL; + mi_register_exit(); +} + +const char *mi_get_xterm_exe() +{ + if (!xterm_exe) + {/* Look for xterm in path */ + xterm_exe=mi_search_in_path("xterm"); + if (!xterm_exe) + return "/usr/bin/X11/xterm"; + } + return xterm_exe; +} + +void mi_set_main_func(const char *name) +{ + free(main_func); + main_func=name ? strdup(name) : NULL; + mi_register_exit(); +} + +const char *mi_get_main_func() +{ + if (main_func) + return main_func; + return "main"; +} + +/**[txh]******************************************************************** + + Description: + Opens a new xterm to be used by the child process to debug. + + Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to +release it. + +***************************************************************************/ + +mi_aux_term *gmi_start_xterm() +{ + char nsh[14]="/tmp/shXXXXXX"; + char ntt[14]="/tmp/ttXXXXXX"; + const char *xterm; + struct stat st; + int hsh, htt=-1; + mi_aux_term *res=NULL; + FILE *f; + pid_t pid; + char buf[PATH_MAX]; + + /* Verify we have an X terminal. */ + xterm=mi_get_xterm_exe(); + if (access(xterm,X_OK)) + { + mi_error=MI_MISSING_XTERM; + return NULL; + } + + /* Create 2 temporals. */ + hsh=mkstemp(nsh); + if (hsh==-1) + { + mi_error=MI_CREATE_TEMPORAL; + return NULL; + } + htt=mkstemp(ntt); + if (htt==-1) + { + close(hsh); + unlink(nsh); + mi_error=MI_CREATE_TEMPORAL; + return NULL; + } + close(htt); + /* Create the script. */ + f=fdopen(hsh,"w"); + if (!f) + { + close(hsh); + unlink(nsh); + unlink(ntt); + mi_error=MI_CREATE_TEMPORAL; + return NULL; + } + fprintf(f,"#!/bin/sh\n"); + fprintf(f,"tty > %s\n",ntt); + fprintf(f,"rm %s\n",nsh); + fprintf(f,"sleep 365d\n"); + fclose(f); + /* Spawn xterm. */ + /* Create the child. */ + pid=fork(); + if (pid==0) + {/* We are the child. */ + char *argv[5]; + /* Pass the control to gdb. */ + argv[0]=(char *)mi_get_xterm_exe(); /* Is that ok? */ + argv[1]="-e"; + argv[2]="/bin/sh"; + argv[3]=nsh; + argv[4]=0; + execvp(argv[0],argv); + /* We get here only if exec failed. */ + unlink(nsh); + unlink(ntt); + _exit(127); + } + /* We are the parent. */ + if (pid==-1) + {/* Fork failed. */ + unlink(nsh); + unlink(ntt); + mi_error=MI_FORK; + return NULL; + } + /* Wait until the shell is deleted. */ + while (stat(nsh,&st)==0) + usleep(1000); + /* Try to read the tty name. */ + f=fopen(ntt,"rt"); + if (f) + { + if (fgets(buf,PATH_MAX,f)) + { + char *s; /* Strip the \n. */ + for (s=buf; *s && *s!='\n'; s++); + *s=0; + res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); + if (res) + { + res->pid=pid; + res->tty=strdup(buf); + } + } + fclose(f); + } + unlink(ntt); + return res; +} + +void mi_free_aux_term(mi_aux_term *t) +{ + if (!t) + return; + free(t->tty); + free(t); +} + +/**[txh]******************************************************************** + + Description: + Closes the auxiliar terminal and releases the allocated memory. + +***************************************************************************/ + +void gmi_end_aux_term(mi_aux_term *t) +{ + if (!t) + return; + if (t->pid!=-1 && mi_check_running_pid(t->pid)) + mi_kill_child(t->pid); + mi_free_aux_term(t); +} + +/**[txh]******************************************************************** + + Description: + Forces the MI version. Currently the library can't detect it so you must +force it manually. GDB 5.x implemented MI v1 and 6.x v2. + +***************************************************************************/ + +void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, + unsigned vMinor) +{ + h->version=MI_VERSION2U(vMajor,vMiddle,vMinor); +} + +/**[txh]******************************************************************** + + Description: + Dis/Enables the @var{wa} workaround for a bug in gdb. + +***************************************************************************/ + +void mi_set_workaround(unsigned wa, int enable) +{ + switch (wa) + { + case MI_PSYM_SEARCH: + disable_psym_search_workaround=enable ? 0 : 1; + break; + } +} + +/**[txh]******************************************************************** + + Description: + Finds if the @var{wa} workaround for a bug in gdb is enabled. + + Return: !=0 if enabled. + +***************************************************************************/ + +int mi_get_workaround(unsigned wa) +{ + switch (wa) + { + case MI_PSYM_SEARCH: + return disable_psym_search_workaround==0; + } + return 0; +} + diff --git a/src/cpp_int.cc b/src/cpp_int.cc new file mode 100644 index 0000000..815d6f7 --- /dev/null +++ b/src/cpp_int.cc @@ -0,0 +1,1123 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2007 by Salvador E. Tropea. + Covered by the GPL license. + + Module: C++ Interface. + Comments: + Implements a very simple (naive ;-) C++ wrapper.@p + +***************************************************************************/ + +#include +#include +#include "mi_gdb.h" + +/**[txh]******************************************************************** + + Description: + Initializes a debugger object. It starts in the "disconnected" state. +Use @x{::Connect} after it. + +***************************************************************************/ + +MIDebugger::MIDebugger() +{ + state=disconnected; + h=NULL; + aux_tty=NULL; + waitingTempBkpt=0; + targetEndian=enUnknown; + targetArch=arUnknown; +} + +/**[txh]******************************************************************** + + Description: + This is the destructor for the class. It tries to change the state to +"disconnected" doing the needed actions. + +***************************************************************************/ + +MIDebugger::~MIDebugger() +{ + if (state==running) + { + Stop(); + mi_stop *rs; + // TODO: Some kind of time-out + while (!Poll(rs)); + mi_free_stop(rs); + state=stopped; + } + if (state==stopped) + { + Kill(); + state=target_specified; + } + if (state==target_specified) + { + TargetUnselect(); + state=connected; + } + if (state==connected) + Disconnect(); + // Here state==disconnected +} + +/**[txh]******************************************************************** + + Description: + Connects to gdb. Currently only local connections are supported, that's +a gdb limitation. Call it when in "unconnected" state, on success it will +change to the "connected" state. After it you should call one of the +SelectTarget members. @x{::SelectTargetX11}, @x{::SelectTargetLinux} or +@x{::SelectTargetRemote}. To finish the connection use @x{::Disconnect}. + + Return: !=0 OK. + +***************************************************************************/ + +int MIDebugger::Connect(bool ) +{ + if (state==disconnected) + { + h=mi_connect_local(); + if (h!=NULL) + { + state=connected; + return 1; + } + } + return 0; +} + +/**[txh]******************************************************************** + + Description: + Finishes the connection to gdb. Call when in "connected" state, on success +it will change to "disconnected" state. This function first tries to exit +from gdb and then close the connection. But if gdb fails to exit it will be +killed. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::Disconnect() +{ + if (state==connected) + { + gmi_gdb_exit(h); + mi_disconnect(h); + state=disconnected; + return 1; + } + return 0; +} + +/**[txh]******************************************************************** + + Description: + Protected member that implements @x{::SelectTargetX11} and +@x{::SelectTargetLinux}. + + Return: !=0 OK. + +***************************************************************************/ + +int MIDebugger::SelectTargetTTY(const char *exec, const char *args, + const char *auxtty, dMode m) +{ + if (state!=connected) + return 0; + + targetEndian=enUnknown; + targetArch=arUnknown; + mode=m; + if (!gmi_set_exec(h,exec,args)) + return 0; + + const char *tty_name; + #ifndef __CYGWIN__ + if (!auxtty) + { + aux_tty=m==dmLinux ? gmi_look_for_free_vt() : gmi_start_xterm(); + if (!aux_tty) + return 0; + tty_name=aux_tty->tty; + } + else + { + tty_name=auxtty; + } + if (!gmi_target_terminal(h,tty_name)) + return 0; + #else + tty_name=NULL; + if (!gmi_gdb_set(h,"new-console","on")) + return 0; + #endif + + state=target_specified; + preRun=false; + return 1; +} + +/**[txh]******************************************************************** + + Description: + Starts a debug session for X11. It opens an xterm console for the program +to debug and tells gdb which executable to debug and the command line +options to pass. You can specify an already existing tty console to be used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use @x{::Run} or use the members +to define breakpoints and similar stuff. To finish it use +@x{::TargetUnselect}. + + Return: !=0 OK. + +***************************************************************************/ + +int MIDebugger::SelectTargetX11(const char *exec, const char *args, + const char *auxtty) +{ + return SelectTargetTTY(exec,args,auxtty,dmX11); +} + + +/**[txh]******************************************************************** + + Description: + Starts a debug session for Linux console. It selects an empty VT for the +program to debug and tells gdb which executable to debug and the command line +options to pass. You can specify an already existing tty console to be used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use @x{::Run} or use the members +to define breakpoints and similar stuff. To finish it use +@x{::TargetUnselect}. + + Return: !=0 OK. + +***************************************************************************/ + +int MIDebugger::SelectTargetLinux(const char *exec, const char *args, + const char *auxtty) +{ + return SelectTargetTTY(exec,args,auxtty,dmLinux); +} + +/**[txh]******************************************************************** + + Description: + Starts a remote session. The other end should be running gdbserver. You +must specify a local copy of the program to debug with debug info. The remote +copy can be stripped. The @var{rtype} and @var{rparams} selects the protocol +and the remote machine. Read gdb docs to know more about the available +options. If @var{rtype} is omitted "extended-remote" protocol is used. +Can be called when the state is "connected". On success will change to the +"target_specified" state. After it you can use @x{::Run} or use the members +to define breakpoints and similar stuff. To finish it use +@x{::TargetUnselect}. Note that when gdb uses remote debugging the remote +program starts running. The @x{::Run} member knows about it. + + Return: !=0 OK. + Example: + o->SelectTargetRemote("./exec_file","192.168.1.65:5000"); + +***************************************************************************/ + +int MIDebugger::SelectTargetRemote(const char *exec, const char *rparams, + const char *rtype, bool download) +{ + if (state!=connected) + return 0; + + mode=dmRemote; + preRun=true; + targetEndian=enUnknown; + targetArch=arUnknown; + if (rtype==NULL) + rtype="extended-remote"; + + /* Tell gdb to load symbols from the local copy. */ + int res=download ? gmi_set_exec(h,exec,NULL) : gmi_file_symbol_file(h,exec); + if (!res) + return 0; + /* Select the target */ + if (!gmi_target_select(h,rtype,rparams)) + return 0; + /* Download the binary */ + if (download) + { + if (!gmi_target_download(h)) + return 0; + } + + state=target_specified; + return 1; +} + +/**[txh]******************************************************************** + + Description: + Starts a local session using an already running process. + + Return: !=0 OK. + +***************************************************************************/ + +mi_frames *MIDebugger::SelectTargetPID(const char *exec, int pid) +{ + if (state!=connected) + return NULL; + + mode=dmPID; + preRun=false; + targetEndian=enUnknown; + targetArch=arUnknown; + + mi_frames *res=gmi_target_attach(h,pid); + if (res) + { + state=stopped; + + /* Tell gdb to load symbols from the local copy. */ + if (!gmi_file_symbol_file(h,exec)) + { + mi_free_frames(res); + return NULL; + } + } + + return res; +} + +/**[txh]******************************************************************** + + Description: + Used to unselect the current target. When X11 mode it closes the auxiliar +terminal. For remote debugging it uses "detach". Can be called when in +"target_specified" state. On success it changes to "connected" state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::TargetUnselect() +{ + switch (mode) + { + case dmX11: + case dmLinux: + if (state!=target_specified) + return 0; + if (aux_tty) + { + gmi_end_aux_term(aux_tty); + aux_tty=NULL; + } + break; + case dmPID: + case dmRemote: + if (state!=target_specified) + { + if (state!=stopped || !gmi_target_detach(h)) + return 0; + } + break; + } + state=connected; + return 1; +} + +/**[txh]******************************************************************** + + Description: + Starts running the program. You should set breakpoint before it. Can be +called when state is "target_specified". On success will change to "running" +state. After it you should poll for async responses using @x{::Poll}. The +program can stop for many reasons asynchronously and also exit. This +information is known using Poll. You can stop the program using @x{::Stop}. + + Return: !=0 OK. + +***************************************************************************/ + +int MIDebugger::Run() +{ + if (state!=target_specified) + return 0; + + int res; + if (preRun) + res=gmi_exec_continue(h); + else + res=gmi_exec_run(h); + if (res) + state=running; + + return res; +} + +/**[txh]******************************************************************** + + Description: + Stops the program execution. GDB sends an interrupt signal to the program. +Can be called when the state is "running". It won't switch to "stopped" +state automatically. Instead you must poll for async events and wait for a +stopped notification. After it you can call @x{::Continue} to resume +execution. + + Return: + Example: !=0 OK + +***************************************************************************/ + +int MIDebugger::Stop() +{ + if (state!=running) + return 0; + return gmi_exec_interrupt(h); +} + +/**[txh]******************************************************************** + + Description: + Polls gdb looking for async responses. Currently it just looks for +"stopped" messages. You must call it when the state is "running". But the +function will poll gdb even if the state isn't "running". When a stopped +message is received the state changes to stopped or target_specified (the +last is when we get some exit). + + Return: !=0 if we got a response. The @var{rs} pointer will point to an +mi_stop structure if we got it or will be NULL if we didn't. + +***************************************************************************/ + +int MIDebugger::Poll(mi_stop *&rs) +{ + if (state==disconnected || !mi_get_response(h)) + return 0; + + mi_stop *res=mi_res_stop(h); + if (res) + { + if (res->reason==sr_exited_signalled || + res->reason==sr_exited || + res->reason==sr_exited_normally) + // When we use a PID the exit makes it invalid, so we don't have a + // valid target to re-run. + state=mode==dmPID ? connected : target_specified; + else + state=stopped; + if (res->reason==sr_unknown && waitingTempBkpt) + { + waitingTempBkpt=0; + res->reason=sr_bkpt_hit; + } + } + else + {// We got an error. It looks like most async commands returns running even + // before they are sure the process is running. Latter we get the real + // error. So I'm assuming the program is stopped. + // Lamentably -target-exec-status isn't implemented and even in this case + // if the program is really running as real async isn't implemented it + // will fail anyways. + if (state==running) + state=stopped; + } + rs=res; + return 1; +} + +/**[txh]******************************************************************** + + Description: + Resumes execution after the program "stopped". Can be called when the state +is stopped. On success will change to "running" state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::Continue() +{ + if (state!=stopped) + return 0; + int res=gmi_exec_continue(h); + if (res) + state=running; + return res; +} + +/**[txh]******************************************************************** + + Description: + Starts program execution or resumes it. When the state is target_specified +it calls @x{::Run} otherwise it uses @x{::Continue}. Can be called when the +state is "target_specified" or "stopped". On success will change to +"running" state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::RunOrContinue() +{ + if (state==target_specified) + return Run(); + return Continue(); +} + +/**[txh]******************************************************************** + + Description: + Kills the program you are debugging. Can be called when the state is +"stopped" or "running". On success changes the state to "target_specified". +Note that if you want to restart the program you can just call @x{::Run} and +if you want to just stop the program call @x{::Stop}. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::Kill() +{ + if (state!=stopped && state!=running) + return 0; + /* GDB/MI doesn't implement it (yet), so we use the regular kill. */ + /* Ensure confirm is off. */ + char *prev=gmi_gdb_show(h,"confirm"); + if (!prev) + return 0; + if (strcmp(prev,"off")) + { + if (!gmi_gdb_set(h,"confirm","off")) + { + free(prev); + return 0; + } + } + else + { + free(prev); + prev=NULL; + } + /* Do the kill. */ + int res=gmi_exec_kill(h); + /* Revert confirm option if needed. */ + if (prev) + { + gmi_gdb_set(h,"confirm",prev); + free(prev); + } + + if (res) + state=target_specified; + + return res; +} + +/**[txh]******************************************************************** + + Description: + Inserts a breakpoint at @var{file} and @var{line}. Can be called when the +state is "stopped" or "target_specified". + + Return: An mi_bkpt structure or NULL if error. + +***************************************************************************/ + +mi_bkpt *MIDebugger::Breakpoint(const char *file, int line) +{ + if (state!=stopped && state!=target_specified) + return NULL; + return gmi_break_insert(h,file,line); +} + +/**[txh]******************************************************************** + + Description: + Inserts a breakpoint at @var{where}, all options available. Can be called +when the state is "stopped" or "target_specified". + + Return: An mi_bkpt structure or NULL if error. + +***************************************************************************/ + +mi_bkpt *MIDebugger::Breakpoint(const char *where, bool temporary, + const char *cond, int count, int thread, + bool hard_assist) +{ + if (state!=stopped && state!=target_specified) + return NULL; + return gmi_break_insert_full(h,temporary,hard_assist,cond,count,thread,where); +} + + +const int maxWhere=PATH_MAX+256; + +mi_bkpt *MIDebugger::Breakpoint(mi_bkpt *b) +{ + if (state!=stopped && state!=target_specified) + return NULL; + + char buf[maxWhere]; + buf[0]=0; + switch (b->mode) + { + case m_file_line: + snprintf(buf,maxWhere,"%s:%d",b->file,b->line); + break; + case m_function: + snprintf(buf,maxWhere,"%s",b->func); + break; + case m_file_function: + snprintf(buf,maxWhere,"%s:%s",b->file,b->func); + break; + case m_address: + snprintf(buf,maxWhere,"*%p",b->addr); + break; + } + return Breakpoint(buf,b->disp==d_del,b->cond,b->ignore,b->thread, + b->type==t_hw); +} + +/**[txh]******************************************************************** + + Description: + Inserts a breakpoint at @var{file} and @var{line} all options available. +Can be called when the state is "stopped" or "target_specified". + + Return: An mi_bkpt structure or NULL if error. + +***************************************************************************/ + +mi_bkpt *MIDebugger::BreakpointFull(const char *file, int line, + bool temporary, const char *cond, + int count, int thread, bool hard_assist) +{ + if (state!=stopped && state!=target_specified) + return NULL; + return gmi_break_insert_full_fl(h,file,line,temporary,hard_assist,cond, + count,thread); +} + +/**[txh]******************************************************************** + + Description: + Removes the specified breakpoint. It doesn't free the structure. Can be +called when the state is "stopped" or "target_specified". + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::BreakDelete(mi_bkpt *b) +{ + if ((state!=stopped && state!=target_specified) || !b) + return 0; + return gmi_break_delete(h,b->number); +} + +/**[txh]******************************************************************** + + Description: + Inserts a watchpoint for the specified expression. Can be called when the +state is "stopped" or "target_specified". + + Return: An mi_wp structure or NULL if error. + +***************************************************************************/ + +mi_wp *MIDebugger::Watchpoint(enum mi_wp_mode mode, const char *exp) +{ + if (state!=stopped && state!=target_specified) + return NULL; + return gmi_break_watch(h,mode,exp); +} + +/**[txh]******************************************************************** + + Description: + Removes the specified watchpoint. It doesn't free the structure. Can be +called when the state is "stopped" or "target_specified". + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::WatchDelete(mi_wp *w) +{ + if ((state!=stopped && state!=target_specified) || !w) + return 0; + return gmi_break_delete(h,w->number); +} + +/**[txh]******************************************************************** + + Description: + Puts a temporal breakpoint in main function and starts running. Can be +called when the state is "target_specified". If successful the state will +change to "running". + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::RunToMain() +{ + if (state!=target_specified) + return 0; + mi_bkpt *b=Breakpoint(mi_get_main_func(),true); + if (!b) + return 0; + mi_free_bkpt(b); + waitingTempBkpt=1; + return Run(); +} + +/**[txh]******************************************************************** + + Description: + Executes upto the next line, doesn't follow function calls. The @var{inst} +argument is for assembler. If the state is "target_specified" it will go to +the first line in the main function. If the state is "stopped" will use the +next command. If successfully the state will change to "running". + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::StepOver(bool inst) +{ + int res=0; + + if (state==target_specified) + {// We aren't running + // Walk to main + return RunToMain(); + } + if (state==stopped) + { + if (inst) + res=gmi_exec_next_instruction(h); + else + res=gmi_exec_next(h); + if (res) + state=running; + } + return res; +} + +/**[txh]******************************************************************** + + Description: + Executes until the specified point. If the state is "target_specified" it +uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. +Fails for any other state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::GoTo(const char *file, int line) +{ + int res=0; + + if (state==target_specified) + {// We aren't running + // Use a temporal breakpoint + int l=strlen(file)+32; + char buf[l]; + snprintf(buf,l,"%s:%d",file,line); + mi_bkpt *b=Breakpoint(buf,true); + if (b) + { + mi_free_bkpt(b); + waitingTempBkpt=1; + res=Run(); + } + } + else if (state==stopped) + { + res=gmi_exec_until(h,file,line); + if (res) + state=running; + } + return res; +} + +/**[txh]******************************************************************** + + Description: + Executes until the specified point. If the state is "target_specified" it +uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. +Fails for any other state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::GoTo(void *addr) +{ + int res=0; + + if (state==target_specified) + {// We aren't running + // Use a temporal breakpoint + char buf[32]; + snprintf(buf,32,"*%p",addr); + mi_bkpt *b=Breakpoint(buf,true); + if (b) + { + mi_free_bkpt(b); + waitingTempBkpt=1; + res=Run(); + } + } + else if (state==stopped) + { + res=gmi_exec_until_addr(h,addr); + if (res) + state=running; + } + return res; +} + + +/**[txh]******************************************************************** + + Description: + Resumes execution until the end of the current funtion is reached. Only +usable when we are in the "stopped" state. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::FinishFun() +{ + if (state!=stopped) + return 0; + int res=gmi_exec_finish(h); + if (res) + state=running; + return res; +} + +/**[txh]******************************************************************** + + Description: + Returns immediately. Only usable when we are in the "stopped" state. + + Return: !=NULL OK, the returned frame is the current location. That's a +synchronous function. + +***************************************************************************/ + +mi_frames *MIDebugger::ReturnNow() +{ + if (state!=stopped) + return 0; + return gmi_exec_return(h); +} + +/**[txh]******************************************************************** + + Description: + Returns the current list of frames. + + Return: !=NULL OK, the list of frames is returned. + +***************************************************************************/ + +mi_frames *MIDebugger::CallStack(bool args) +{ + if (state!=stopped) + return 0; + mi_frames *fr1=gmi_stack_list_frames(h); + if (fr1 && args) + {// Get the function arguments + mi_frames *fr2=gmi_stack_list_arguments(h,1); + if (fr2) + {// Transfer them to the other list + mi_frames *p=fr1, *p2=fr2; + while (p2 && p) + { + p->args=p2->args; + p2->args=NULL; + p2=p2->next; + p=p->next; + } + mi_free_frames(fr2); + } + } + return fr1; +} + +/**[txh]******************************************************************** + + Description: + Executes upto the next line, it follows function calls. The @var{inst} +argument is for assembler. If the state is "target_specified" it will go to +the first line in the main function. If the state is "stopped" will use the +next command. If successfully the state will change to "running". + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::TraceInto(bool inst) +{ + int res=0; + + if (state==target_specified) + {// We aren't running + // Walk to main + return RunToMain(); + } + if (state==stopped) + { + if (inst) + res=gmi_exec_step_instruction(h); + else + res=gmi_exec_step(h); + if (res) + state=running; + } + return res; +} + +/**[txh]******************************************************************** + + Description: + Evaluates the provided expression. If we get an error the error +description is returned instead. Can't be called if "disconnected" or +"running". + + Return: The result of the expression (use free) or NULL. + +***************************************************************************/ + +char *MIDebugger::EvalExpression(const char *exp) +{ + if (state==disconnected || + state==running) // No async :-( + return NULL; + // Evaluate it + mi_error=MI_OK; + char *res=gmi_data_evaluate_expression(h,exp); + if (!res && mi_error_from_gdb) + {// Not valid, return the error + res=strdup(mi_error_from_gdb); + } + return res; +} + +/**[txh]******************************************************************** + + Description: + Modifies the provided expression. If we get an error the error +description is returned instead. Can't be called if "disconnected" or +"running". + + Return: The result of the expression (use free) or NULL. + +***************************************************************************/ + +char *MIDebugger::ModifyExpression(char *exp, char *newVal) +{ + if (state==disconnected || + state==running) // No async :-( + return NULL; + // Create an assignment + int l1=strlen(exp); + int l2=strlen(newVal); + char b[l1+l2+2], *s=b; + memcpy(s,exp,l1); + s+=l1; + *s='='; + memcpy(++s,newVal,l2); + s[l2]=0; + // Evaluate it + char *res=gmi_data_evaluate_expression(h,b); + if (!res && mi_error_from_gdb) + {// Not valid, return the error + res=strdup(mi_error_from_gdb); + } + return res; +} + +/**[txh]******************************************************************** + + Description: + Sends a command to gdb. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::Send(const char *command) +{ + if (state==disconnected || + state==running) // No async :-( + return 0; + // TODO: detect and use -interpreter-exec? + mi_send(h,"%s\n",command); + return mi_res_simple_done(h); +} + + +/**[txh]******************************************************************** + + Description: + Fills the type and value fields of the mi_gvar provided list. + + Return: !=0 OK + +***************************************************************************/ + +int MIDebugger::FillTypeVal(mi_gvar *var) +{ + while (var) + { + if (!var->type && !gmi_var_info_type(h,var)) + return 0; + if (!var->value && !gmi_var_evaluate_expression(h,var)) + return 0; + var=var->next; + } + return 1; +} + +int MIDebugger::FillOneTypeVal(mi_gvar *var) +{ + if (!var) + return 0; + + int ok=1; + if (!var->type && !gmi_var_info_type(h,var)) + { + var->type=strdup(""); + ok=0; + } + if (!var->value && !gmi_var_evaluate_expression(h,var)) + { + var->value=strdup(""); + ok=0; + } + return ok; +} + +int MIDebugger::AssigngVar(mi_gvar *var, const char *exp) +{ + if (state!=stopped) + return 0; + return gmi_var_assign(h,var,exp); +} + +char *MIDebugger::Show(const char *var) +{ + if (state==running || state==disconnected) + return 0; + // GDB 5.x doesn't reply all in the response record, just to the console :-( + h->catch_console=1; + if (h->catched_console) + { + free(h->catched_console); + h->catched_console=NULL; + } + char *res=gmi_gdb_show(h,var); + h->catch_console=0; + if (!res && h->catched_console) + { + res=h->catched_console; + h->catched_console=NULL; + } + return res; +} + +MIDebugger::endianType MIDebugger::GetTargetEndian() +{ + if (targetEndian!=enUnknown) + return targetEndian; + if (state!=stopped && state!=target_specified) + return enUnknown; + + char *end=Show("endian"); + if (end) + { + if (strstr(end,"big")) + targetEndian=enBig; + else if (strstr(end,"little")) + targetEndian=enLittle; + free(end); + } + return targetEndian; +} + +MIDebugger::archType MIDebugger::GetTargetArchitecture() +{ + if (targetArch!=arUnknown) + return targetArch; + if (state!=stopped && state!=target_specified) + return arUnknown; + + char *end=Show("architecture"); + if (end) + { + if (strstr(end,"i386")) + targetArch=arIA32; + else if (strstr(end,"sparc")) + targetArch=arSPARC; + else if (strstr(end,"pic14")) + targetArch=arPIC14; + else if (strstr(end,"avr")) + targetArch=arAVR; + free(end); + } + return targetArch; +} + +int MIDebugger::GetErrorNumberSt() +{ + if (mi_error==MI_GDB_DIED) + { + state=target_specified; + TargetUnselect(); + state=connected; + Disconnect(); + } + return mi_error; +} + +int MIDebugger::UpdateRegisters(mi_chg_reg *regs) +{ + int updated=0; + mi_chg_reg *chg=GetChangedRegisters(); + if (chg) + { + mi_chg_reg *r=regs, *c; + while (r) + { + c=chg; + while (c && c->reg!=r->reg) + c=c->next; + if (c) + { + r->updated=1; + free(r->val); + r->val=c->val; + c->val=NULL; + updated++; + } + else + r->updated=0; + r=r->next; + } + } + return updated; +} + diff --git a/src/data_man.c b/src/data_man.c new file mode 100644 index 0000000..a7e9f11 --- /dev/null +++ b/src/data_man.c @@ -0,0 +1,241 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Data manipulation. + Comments: + GDB/MI commands for the "Data manipulation" section.@p + +@
    +gdb command:                       Implemented?
    +
    +-data-disassemble                  Yes
    +-data-evaluate-expression          Yes
    +-data-list-changed-registers       No
    +-data-list-register-names          Yes
    +-data-list-register-values         No
    +-data-read-memory                  No
    +-display-delete                    N.A. (delete display)
    +-display-disable                   N.A. (disable display)
    +-display-enable                    N.A. (enable display)
    +-display-insert                    N.A. (display)
    +-display-list                      N.A. (info display)
    +-environment-cd                    No
    +-environment-directory             Yes, MI v1 implementation
    +-environment-path                  No
    +@
    + +Notes:@p + +1) -display* aren't implemented. You can use CLI command display, but the +results are sent to the console. So it looks like the best is to manually +use -data-evaluate-expression to emulate it.@p + +2) GDB bug mi/1770: Affects gdb<=6.2, when you ask for the names of the +registers you get it plus the name of the "pseudo-registers", but if you +try to get the value of a pseudo-register you get an error saying the +register number is invalid. I reported to gdb-patches@sources.redhat.com +on 2004/08/25 and as I didn't get any answer I filled a bug report on +2004/09/02. The patch to fix this annoying bug is: + +Index: gdb/mi/mi-main.c +=================================================================== +RCS file: /cvs/src/src/gdb/mi/mi-main.c,v +retrieving revision 1.64 +diff -u -r1.64 mi-main.c +--- gdb/mi/mi-main.c 3 Aug 2004 00:57:27 -0000 1.64 ++++ gdb/mi/mi-main.c 25 Aug 2004 14:12:50 -0000 +@@ -423,7 +423,7 @@ + case, some entries of REGISTER_NAME will change depending upon + the particular processor being debugged. + +- numregs = NUM_REGS; ++ numregs = NUM_REGS + NUM_PSEUDO_REGS; + + if (argc == 0) + { +---- + +Note I had to remove an end of comment in the patch to include it here. +This bug forced me to create another set of functions. The only way is to +first get the values and then the names. +Fixed by Changelog entry: + +2004-09-12 Salvador E. Tropea + Andrew Cagney + + * mi/mi-main.c (mi_cmd_data_list_changed_registers) + (mi_cmd_data_list_register_values) + (mi_cmd_data_write_register_values): Include the PSEUDO_REGS in + the register number computation. + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_data_evaluate_expression(mi_h *h, const char *expression) +{ + mi_send(h,"-data-evaluate-expression \"%s\"\n",expression); +} + +void mi_dir(mi_h *h, const char *path) +{ + if (h->version>=MI_VERSION2U(2,0,0)) + {// MI v2 + if (path) + mi_send(h,"-environment-directory \"%s\"\n",path); + else + mi_send(h,"-environment-directory -r\n"); + } + else + { + mi_send(h,"-environment-directory %s\n",path ? path : ""); + } +} + +void mi_data_read_memory_hx(mi_h *h, const char *exp, unsigned ws, + unsigned c, int convAddr) +{ + if (convAddr) + mi_send(h,"-data-read-memory \"&%s\" x %d 1 %d\n",exp,ws,c); + else + mi_send(h,"-data-read-memory \"%s\" x %d 1 %d\n",exp,ws,c); +} + +void mi_data_disassemble_se(mi_h *h, const char *start, const char *end, + int mode) +{ + mi_send(h,"-data-disassemble -s \"%s\" -e \"%s\" -- %d\n",start,end,mode); +} + +void mi_data_disassemble_fl(mi_h *h, const char *file, int line, int lines, + int mode) +{ + mi_send(h,"-data-disassemble -f \"%s\" -l %d -n %d -- %d\n",file,line,lines, + mode); +} + +void mi_data_list_register_names(mi_h *h) +{ + mi_send(h,"-data-list-register-names\n"); +} + +void mi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) +{ + mi_send(h,"-data-list-register-names "); + while (l) + { + mi_send(h,"%d ",l->reg); + l=l->next; + } + mi_send(h,"\n"); +} + +void mi_data_list_changed_registers(mi_h *h) +{ + mi_send(h,"-data-list-changed-registers\n"); +} + +void mi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) +{ + mi_send(h,"-data-list-register-values %c ",mi_format_enum_to_char(fmt)); + while (l) + { + mi_send(h,"%d ",l->reg); + l=l->next; + } + mi_send(h,"\n"); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Evaluate an expression. Returns a parsed tree. + + Command: -data-evaluate-expression + Return: The resulting value (as plain text) or NULL on error. + +***************************************************************************/ + +char *gmi_data_evaluate_expression(mi_h *h, const char *expression) +{ + mi_data_evaluate_expression(h,expression); + return mi_res_value(h); +} + +/**[txh]******************************************************************** + + Description: + Path for sources. You must use it to indicate where are the sources for +the program to debug. Only the MI v1 implementation is available. + + Command: -environment-directory + Return: !=0 OK + +***************************************************************************/ + +int gmi_dir(mi_h *h, const char *path) +{ + mi_dir(h,path); + return mi_res_simple_done(h); +} + +int gmi_read_memory(mi_h *h, const char *exp, unsigned size, + unsigned char *dest, int *na, int convAddr, + unsigned long *addr) +{ + mi_data_read_memory_hx(h,exp,1,size,convAddr); + return mi_get_read_memory(h,dest,1,na,addr); +} + +mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, + const char *end, int mode) +{ + mi_data_disassemble_se(h,start,end,mode); + return mi_get_asm_insns(h); +} + +mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, + int lines, int mode) +{ + mi_data_disassemble_fl(h,file,line,lines,mode); + return mi_get_asm_insns(h); +} + +// Affected by gdb bug mi/1770 +mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many) +{ + mi_data_list_register_names(h); + return mi_get_list_registers(h,how_many); +} + +int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) +{ + mi_data_list_register_names_l(h,l); + return mi_get_list_registers_l(h,l); +} + +mi_chg_reg *gmi_data_list_changed_registers(mi_h *h) +{ + mi_error=MI_OK; + mi_data_list_changed_registers(h); + return mi_get_list_changed_regs(h); +} + +int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) +{ + mi_data_list_register_values(h,fmt,l); + return mi_get_reg_values(h,l); +} + +mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many) +{ + mi_data_list_register_values(h,fmt,NULL); + return mi_get_reg_values_l(h,how_many); +} + diff --git a/src/error.c b/src/error.c new file mode 100644 index 0000000..adeb16c --- /dev/null +++ b/src/error.c @@ -0,0 +1,38 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Error. + Comment: + Translates error numbers into messages. + +***************************************************************************/ + +#include "mi_gdb.h" + +static +const char *error_strs[]= +{ + "Ok", + "Out of memory", + "Pipe creation", + "Fork failed", + "GDB not running", + "Parser failed", + "Unknown asyn response", + "Unknown result response", + "Error from gdb", + "Time out in gdb response", + "GDB suddenly died", + "Can't execute X terminal", + "Failed to create temporal", + "Can't execute the debugger" +}; + +const char *mi_get_error_str() +{ + if (mi_error<0 || mi_error>MI_LAST_ERROR) + return "Unknown"; + return error_strs[mi_error]; +} diff --git a/src/get_free_pty.c b/src/get_free_pty.c new file mode 100644 index 0000000..8c145d0 --- /dev/null +++ b/src/get_free_pty.c @@ -0,0 +1,132 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: pseudo terminal + Comments: + Helper to find a free pseudo terminal. Use this if you need to manage + input *and* output to the target process. If you just need output then + define a handler for target output stream records (assuming that this + is working for your particular version of gdb). + Usage: + + mi_pty *pty = gmi_look_for_free_pty(); + if (pty) gmi_target_terminal(mih, pty->slave); + ... + * reading from pty->master will get stdout from target * + * writing to pty->master will send to target stdin * + + Note: Contributed by Greg Watson (gwatson lanl gov) + +***************************************************************************/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include "mi_gdb.h" + +/**[txh]******************************************************************** + + Description: + Look for a free and usable pseudo terminal. Low level, use +@x{gmi_look_for_free_pty}. + + Return: A file descriptor connected to the master pty and the name of the slave device, or <0 on error. + +***************************************************************************/ + +#ifdef __APPLE__ + +#include + +int mi_look_for_free_pty(int *master, char **slave) +{ + int fdmaster; + int fdslave; + static char name[BUFSIZ]; + + if (openpty(&fdmaster,&fdslave,name,NULL,NULL)<0) + return -1; + + (void)close(fdslave); /* this will be reopened by gdb */ + *master=fdmaster; + *slave =name; + + return 0; +} + +#elif defined(__linux__) + +int mi_look_for_free_pty(int *master, char **slave) +{ + if ((*master=open("/dev/ptmx",O_RDWR))<0) + return -1; + if (grantpt(*master)<0 || unlockpt(*master)<0) + return -1; + *slave = ptsname(*master); + + return 0; +} + +#else /* undefined o/s */ + +int mi_look_for_free_pty(int *master, char **slave) +{ + return -1; +} +#endif + +/**[txh]******************************************************************** + + Description: + Look for a free and usable pseudo terminal to be used by the child. + + Return: A new mi_pty structure, you can use @x{gmi_end_pty} to +release it. + +***************************************************************************/ + +mi_pty *gmi_look_for_free_pty() +{ + int master; + char *slave; + int pty=mi_look_for_free_pty(&master,&slave); + mi_pty *res; + + if (pty<0) + return NULL; + res=(mi_pty *)malloc(sizeof(mi_pty)); + if (!res) + return NULL; + res->slave=strdup(slave); + res->master=master; + return res; +} + +void mi_free_pty(mi_pty *p) +{ + if (!p) + return; + free(p->slave); + free(p); +} + +/**[txh]******************************************************************** + + Description: + Closes the pseudo termial master and releases the allocated memory. + +***************************************************************************/ + +void gmi_end_pty(mi_pty *p) +{ + if (!p) + return; + close(p->master); + mi_free_pty(p); +} diff --git a/src/get_free_vt.c b/src/get_free_vt.c new file mode 100644 index 0000000..f5c9800 --- /dev/null +++ b/src/get_free_vt.c @@ -0,0 +1,153 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Linux VT. + Comments: + Helper to find a free VT. That's 100% Linux specific.@p + The code comes from "lconsole.c" from Allegro project and was originally +created by Marek Habersack and then modified by George Foot. I addapted it +to my needs and changed license from giftware to GPL.@p + +***************************************************************************/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#endif /* __APPLE__ */ + +#include "mi_gdb.h" + +#if !defined(__linux__) + +int mi_look_for_free_vt() +{ + return -1; +} + +mi_aux_term *gmi_look_for_free_vt() +{ + return NULL; +} + +#else + +#include + +/**[txh]******************************************************************** + + Description: + Look for a free and usable Linux VT. Low level, use +@x{gmi_look_for_free_vt}. + + Return: The VT number or <0 on error. + +***************************************************************************/ + +int mi_look_for_free_vt() +{/* Code from Allegro. */ + int tty, console_fd, fd; + unsigned short mask; + char tty_name[16]; + struct vt_stat vts; + + /* Now we need to find a VT we can use. It must be readable and + * writable by us, if we're not setuid root. VT_OPENQRY itself + * isn't too useful because it'll only ever come up with one + * suggestion, with no guarrantee that we actually have access + * to it. + * + * At some stage I think this is a candidate for config + * file overriding, but for now we'll stat the first N consoles + * to see which ones we can write to (hopefully at least one!), + * so that we can use that one to do ioctls. We used to use + * /dev/console for that purpose but it looks like it's not + * always writable by enough people. + * + * Having found and opened a writable device, we query the state + * of the first sixteen (fifteen really) consoles, and try + * opening each unused one in turn. + */ + + console_fd=open("/dev/console",O_WRONLY); + if (console_fd<0) + { + int n; + /* Try some ttys instead... */ + for (n=1; n<=24; n++) + { + snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",n); + console_fd=open(tty_name,O_WRONLY); + if (console_fd>=0) + break; + } + if (n>24) + return -1; + } + + /* Get the state of the console -- in particular, the free VT field */ + if (ioctl(console_fd,VT_GETSTATE,&vts)) + return -2; + close(console_fd); + + /* We attempt to set our euid to 0; if we were run with euid 0 to + * start with, we'll be able to do this now. Otherwise, we'll just + * ignore the error returned since it might not be a problem if the + * ttys we look at are owned by the user running the program. */ + seteuid(0); + + /* tty0 is not really a console, so start counting at 2. */ + fd=-1; + for (tty=1, mask=2; mask; tty++, mask<<=1) + if (!(vts.v_state & mask)) + { + snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",tty); + fd=open(tty_name,O_RDWR); + if (fd!=-1) + { + close(fd); + break; + } + } + + seteuid(getuid()); + + if (!mask) + return -3; + + return tty; +} + +/**[txh]******************************************************************** + + Description: + Look for a free and usable Linux VT to be used by the child. + + Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to +release it. + +***************************************************************************/ + +mi_aux_term *gmi_look_for_free_vt() +{ + int vt=mi_look_for_free_vt(); + mi_aux_term *res; + + if (vt<0) + return NULL; + res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); + if (!res) + return NULL; + res->pid=-1; + asprintf(&res->tty,"/dev/tty%d",vt); + return res; +} + +#endif + diff --git a/src/mi_gdb.h b/src/mi_gdb.h new file mode 100644 index 0000000..df932b4 --- /dev/null +++ b/src/mi_gdb.h @@ -0,0 +1,972 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2009 by Salvador E. Tropea. + Covered by the GPL license. + + Comments: + Main header for libmigdb. + +***************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include /* pid_t */ + +#define MI_OK 0 +#define MI_OUT_OF_MEMORY 1 +#define MI_PIPE_CREATE 2 +#define MI_FORK 3 +#define MI_DEBUGGER_RUN 4 +#define MI_PARSER 5 +#define MI_UNKNOWN_ASYNC 6 +#define MI_UNKNOWN_RESULT 7 +#define MI_FROM_GDB 8 +#define MI_GDB_TIME_OUT 9 +#define MI_GDB_DIED 10 +#define MI_MISSING_XTERM 11 +#define MI_CREATE_TEMPORAL 12 +#define MI_MISSING_GDB 13 +#define MI_LAST_ERROR 13 + +#define MI_R_NONE 0 /* We are no waiting any response. */ +#define MI_R_SKIP 1 /* We want to discard it. */ +#define MI_R_FE_AND_S 2 /* Wait for done. */ +#define MI_R_E_ARGS 3 + +enum mi_val_type { t_const, t_tuple, t_list }; + +/* Types and subtypes. */ +/* Type. */ +#define MI_T_OUT_OF_BAND 0 +#define MI_T_RESULT_RECORD 1 +/* Out of band subtypes. */ +#define MI_ST_ASYNC 0 +#define MI_ST_STREAM 1 +/* Async sub-subtypes. */ +#define MI_SST_EXEC 0 +#define MI_SST_STATUS 1 +#define MI_SST_NOTIFY 2 +/* Stream sub-subtypes. */ +#define MI_SST_CONSOLE 3 +#define MI_SST_TARGET 4 +#define MI_SST_LOG 5 +/* Classes. */ +/* Async classes. */ +#define MI_CL_UNKNOWN 0 +#define MI_CL_STOPPED 1 +#define MI_CL_DOWNLOAD 2 +/* Result classes. */ +#define MI_CL_DONE 2 +#define MI_CL_RUNNING 3 +#define MI_CL_CONNECTED 4 +#define MI_CL_ERROR 5 +#define MI_CL_EXIT 6 + +#define MI_DEFAULT_TIME_OUT 10 + +#define MI_DIS_ASM 0 +#define MI_DIS_SRC_ASM 1 + +/* Implemented workaround for gdb bugs that we can dis/enable. */ +/* At least gdb<=6.1.1 fails to find a source file with absolute path if the + name is for a psym instead of a sym. psym==partially loaded symbol table. */ +#define MI_PSYM_SEARCH 0 + +#define MI_VERSION_STR "0.8.12" +#define MI_VERSION_MAJOR 0 +#define MI_VERSION_MIDDLE 8 +#define MI_VERSION_MINOR 12 + +struct mi_results_struct +{ + char *var; /* Result name or NULL if just a value. */ + enum mi_val_type type; + union + { + char *cstr; + struct mi_results_struct *rs; + } v; + struct mi_results_struct *next; +}; +typedef struct mi_results_struct mi_results; + +struct mi_output_struct +{ + /* Type of output. */ + char type; + char stype; + char sstype; + char tclass; + /* Content. */ + mi_results *c; + /* Always modeled as a list. */ + struct mi_output_struct *next; +}; +typedef struct mi_output_struct mi_output; + +typedef void (*stream_cb)(const char *, void *); +typedef void (*async_cb)(mi_output *o, void *); +typedef int (*tm_cb)(void *); + +/* Values of this structure shouldn't be manipulated by the user. */ +struct mi_h_struct +{ + /* Pipes connected to gdb. */ + int to_gdb[2]; + int from_gdb[2]; + /* Streams for the pipes. */ + FILE *to, *from; + /* PID of child gdb. */ + pid_t pid; + char died; + /* Which rensponse we are waiting for. */ + /*int response;*/ + /* The line we are reading. */ + char *line; + int llen, lread; + /* Parsed output. */ + mi_output *po, *last; + /* Tunneled streams callbacks. */ + stream_cb console; + void *console_data; + stream_cb target; + void *target_data; + stream_cb log; + void *log_data; + /* Async responses callback. */ + async_cb async; + void *async_data; + /* Callbacks to get echo of gdb dialog. */ + stream_cb to_gdb_echo; + void *to_gdb_echo_data; + stream_cb from_gdb_echo; + void *from_gdb_echo_data; + /* Time out */ + tm_cb time_out_cb; + void *time_out_cb_data; + int time_out; + /* Ugly workaround for some of the show responses :-( */ + int catch_console; + char *catched_console; + /* MI version, currently unknown but the user can force v2 */ + unsigned version; +}; +typedef struct mi_h_struct mi_h; + +#define MI_TO(a) ((a)->to_gdb[1]) + +enum mi_bkp_type { t_unknown=0, t_breakpoint=1, t_hw=2 }; +enum mi_bkp_disp { d_unknown=0, d_keep=1, d_del=2 }; +enum mi_bkp_mode { m_file_line=0, m_function=1, m_file_function=2, m_address=3 }; + +struct mi_bkpt_struct +{ + int number; + enum mi_bkp_type type; + enum mi_bkp_disp disp; /* keep or del if temporal */ + char enabled; + void *addr; + char *func; + char *file; + int line; + int ignore; + int times; + + /* For the user: */ + char *cond; + char *file_abs; + int thread; + enum mi_bkp_mode mode; + struct mi_bkpt_struct *next; +}; +typedef struct mi_bkpt_struct mi_bkpt; + +enum mi_wp_mode { wm_unknown=0, wm_write=1, wm_read=2, wm_rw=3 }; + +struct mi_wp_struct +{ + int number; + char *exp; + enum mi_wp_mode mode; + + /* For the user: */ + struct mi_wp_struct *next; + char enabled; +}; +typedef struct mi_wp_struct mi_wp; + +struct mi_frames_struct +{ + int level; /* The frame number, 0 being the topmost frame, i.e. the innermost + function. */ + void *addr; /* The `$pc' value for that frame. */ + char *func; /* Function name. */ + char *file; /* File name of the source file where the function lives. */ + char *from; + int line; /* Line number corresponding to the `$pc'. */ + /* When arguments are available: */ + mi_results *args; + int thread_id; + /* When more than one is provided: */ + struct mi_frames_struct *next; +}; +typedef struct mi_frames_struct mi_frames; + +struct mi_aux_term_struct +{ + pid_t pid; + char *tty; +}; +typedef struct mi_aux_term_struct mi_aux_term; + +struct mi_pty_struct +{ + char *slave; + int master; +}; +typedef struct mi_pty_struct mi_pty; + +enum mi_gvar_fmt { fm_natural=0, fm_binary=1, fm_decimal=2, fm_hexadecimal=3, + fm_octal=4, + /* Only for registers format: */ + fm_raw=5 }; +enum mi_gvar_lang { lg_unknown=0, lg_c, lg_cpp, lg_java }; + +#define MI_ATTR_DONT_KNOW 0 +#define MI_ATTR_NONEDITABLE 1 +#define MI_ATTR_EDITABLE 2 + +struct mi_gvar_struct +{ + char *name; + int numchild; + char *type; + enum mi_gvar_fmt format; + enum mi_gvar_lang lang; + char *exp; + int attr; + + /* MI v2 fills it, not yet implemented here. */ + /* Use gmi_var_evaluate_expression. */ + char *value; + + /* Pointer to the parent. NULL if none. */ + struct mi_gvar_struct *parent; + /* List containing the children. + Filled by gmi_var_list_children. + NULL if numchild==0 or not yet filled. */ + struct mi_gvar_struct *child; + /* Next var in the list. */ + struct mi_gvar_struct *next; + + /* For the user: */ + char opened; /* We will show its children. 1 when we fill "child" */ + char changed; /* Needs to be updated. 0 when created. */ + int vischild; /* How many items visible. numchild when we fill "child" */ + int depth; /* How deep is this var. */ + char ispointer; +}; +typedef struct mi_gvar_struct mi_gvar; + +struct mi_gvar_chg_struct +{ + char *name; + int in_scope; /* if true the other fields apply. */ + char *new_type; /* NULL if type_changed==false */ + int new_num_children; /* only when new_type!=NULL */ + + struct mi_gvar_chg_struct *next; +}; +typedef struct mi_gvar_chg_struct mi_gvar_chg; + + +/* A list of assembler instructions. */ +struct mi_asm_insn_struct +{ + void *addr; + char *func; + unsigned offset; + char *inst; + + struct mi_asm_insn_struct *next; +}; +typedef struct mi_asm_insn_struct mi_asm_insn; + +/* A list of source lines containing assembler instructions. */ +struct mi_asm_insns_struct +{ + char *file; + int line; + mi_asm_insn *ins; + + struct mi_asm_insns_struct *next; +}; +typedef struct mi_asm_insns_struct mi_asm_insns; + +/* Changed register. */ +struct mi_chg_reg_struct +{ + int reg; + char *val; + char *name; + char updated; + + struct mi_chg_reg_struct *next; +}; +typedef struct mi_chg_reg_struct mi_chg_reg; + +/* + Examining gdb sources and looking at docs I can see the following "stop" +reasons: + +Breakpoints: +a) breakpoint-hit (bkptno) + frame +Also: without reason for temporal breakpoints. + +Watchpoints: +b) watchpoint-trigger (wpt={number,exp};value={old,new}) + frame +c) read-watchpoint-trigger (hw-rwpt{number,exp};value={value}) + frame +d) access-watchpoint-trigger (hw-awpt{number,exp};value={[old,]new}) + frame +e) watchpoint-scope (wpnum) + frame + +Movement: +f) function-finished ([gdb-result-var,return-value]) + frame +g) location-reached + frame +h) end-stepping-range + frame + +Exit: +i) exited-signalled (signal-name,signal-meaning) +j) exited (exit-code) +k) exited-normally + +Signal: +l) signal-received (signal-name,signal-meaning) + frame + +Plus: thread-id +*/ +enum mi_stop_reason +{ + sr_unknown=0, + sr_bkpt_hit, + sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, + sr_function_finished, sr_location_reached, sr_end_stepping_range, + sr_exited_signalled, sr_exited, sr_exited_normally, + sr_signal_received +}; + +struct mi_stop_struct +{ + enum mi_stop_reason reason; /* If more than one reason just the last. */ + /* Flags indicating if non-pointer fields are filled. */ + char have_thread_id; + char have_bkptno; + char have_exit_code; + char have_wpno; + /* Where stopped. Doesn't exist for sr_exited*. */ + int thread_id; + mi_frames *frame; + /* sr_bkpt_hit */ + int bkptno; + /* sr_*wp_* no scope */ + mi_wp *wp; + char *wp_old; + char *wp_val; + /* sr_wp_scope */ + int wpno; + /* sr_function_finished. Not for void func. */ + char *gdb_result_var; + char *return_value; + /* sr_exited_signalled, sr_signal_received */ + char *signal_name; + char *signal_meaning; + /* sr_exited */ + int exit_code; +}; +typedef struct mi_stop_struct mi_stop; + +/* Variable containing the last error. */ +extern int mi_error; +extern char *mi_error_from_gdb; +const char *mi_get_error_str(); + +/* Indicate the name of gdb exe. Default is /usr/bin/gdb */ +void mi_set_gdb_exe(const char *name); +const char *mi_get_gdb_exe(); +/* Indicate the name of a file containing commands to send at start-up */ +void mi_set_gdb_start(const char *name); +const char *mi_get_gdb_start(); +/* Indicate the name of a file containing commands to send after connection */ +void mi_set_gdb_conn(const char *name); +const char *mi_get_gdb_conn(); +void mi_send_target_commands(mi_h *h); +/* Connect to a local copy of gdb. */ +mi_h *mi_connect_local(); +/* Close connection. You should ask gdb to quit first. */ +void mi_disconnect(mi_h *h); +/* Force MI version. */ +#define MI_VERSION2U(maj,mid,min) (maj*0x1000000+mid*0x10000+min) +void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, + unsigned vMinor); +void mi_set_workaround(unsigned wa, int enable); +int mi_get_workaround(unsigned wa); +/* Parse gdb output. */ +mi_output *mi_parse_gdb_output(const char *str); +/* Functions to set/get the tunneled streams callbacks. */ +void mi_set_console_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_target_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_log_cb(mi_h *h, stream_cb cb, void *data); +stream_cb mi_get_console_cb(mi_h *h, void **data); +stream_cb mi_get_target_cb(mi_h *h, void **data); +stream_cb mi_get_log_cb(mi_h *h, void **data); +/* The callback to deal with async events. */ +void mi_set_async_cb(mi_h *h, async_cb cb, void *data); +async_cb mi_get_async_cb(mi_h *h, void **data); +/* Time out in gdb responses. */ +void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data); +tm_cb mi_get_time_out_cb(mi_h *h, void **data); +void mi_set_time_out(mi_h *h, int to); +int mi_get_time_out(mi_h *h); +/* Callbacks to "see" the dialog with gdb. */ +void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data); +stream_cb mi_get_to_gdb_cb(mi_h *h, void **data); +stream_cb mi_get_from_gdb_cb(mi_h *h, void **data); +/* Sends a message to gdb. */ +int mi_send(mi_h *h, const char *format, ...); +/* Wait until gdb sends a response. */ +mi_output *mi_get_response_blk(mi_h *h); +/* Check if gdb sent a complete response. Use with mi_retire_response. */ +int mi_get_response(mi_h *h); +/* Get the last response. Use with mi_get_response. */ +mi_output *mi_retire_response(mi_h *h); +/* Look for a result record in gdb output. */ +mi_output *mi_get_rrecord(mi_output *r); +/* Look if the output contains an async stop. + If that's the case return the reason for the stop. + If the output contains an error the description is returned in reason. */ +int mi_get_async_stop_reason(mi_output *r, char **reason); +mi_stop *mi_get_stopped(mi_results *r); +mi_frames *mi_get_async_frame(mi_output *r); +/* Wait until gdb sends a response. + Then check if the response is of the desired type. */ +int mi_res_simple_exit(mi_h *h); +int mi_res_simple_done(mi_h *h); +int mi_res_simple_running(mi_h *h); +int mi_res_simple_connected(mi_h *h); +/* It additionally extracts an specified variable. */ +mi_results *mi_res_done_var(mi_h *h, const char *var); +/* Extract a frames list from the response. */ +mi_frames *mi_res_frames_array(mi_h *h, const char *var); +mi_frames *mi_res_frames_list(mi_h *h); +mi_frames *mi_parse_frame(mi_results *c); +mi_frames *mi_res_frame(mi_h *h); +/* Create an auxiliar terminal using xterm. */ +mi_aux_term *gmi_start_xterm(); +/* Indicate the name of xterm exe. Default is /usr/bin/X11/xterm */ +void mi_set_xterm_exe(const char *name); +const char *mi_get_xterm_exe(); +/* Kill the auxiliar terminal and release the structure. */ +void gmi_end_aux_term(mi_aux_term *t); +/* Look for a free Linux VT for the child. */ +mi_aux_term *gmi_look_for_free_vt(); +/* Look for a free and usable Linux VT. */ +int mi_look_for_free_vt(); +/* Close master and release the structure. */ +void gmi_end_pty(mi_pty *p); +/* Look for a free pseudo terminal. */ +mi_pty *gmi_look_for_free_pty(); +/* Extract a list of thread IDs from response. */ +int mi_res_thread_ids(mi_h *h, int **list); +int mi_get_thread_ids(mi_output *res, int **list); +/* A variable response. */ +mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression); +enum mi_gvar_fmt mi_format_str_to_enum(const char *format); +const char *mi_format_enum_to_str(enum mi_gvar_fmt format); +char mi_format_enum_to_char(enum mi_gvar_fmt format); +enum mi_gvar_lang mi_lang_str_to_enum(const char *lang); +const char *mi_lang_enum_to_str(enum mi_gvar_lang lang); +int mi_res_changelist(mi_h *h, mi_gvar_chg **changed); +int mi_res_children(mi_h *h, mi_gvar *v); +mi_bkpt *mi_res_bkpt(mi_h *h); +mi_wp *mi_res_wp(mi_h *h); +char *mi_res_value(mi_h *h); +mi_stop *mi_res_stop(mi_h *h); +enum mi_stop_reason mi_reason_str_to_enum(const char *s); +const char *mi_reason_enum_to_str(enum mi_stop_reason r); +int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, + unsigned long *addr); +mi_asm_insns *mi_get_asm_insns(mi_h *h); +/* Starting point of the program. */ +void mi_set_main_func(const char *name); +const char *mi_get_main_func(); +mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many); +int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l); +mi_chg_reg *mi_get_list_changed_regs(mi_h *h); +int mi_get_reg_values(mi_h *h, mi_chg_reg *l); +mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many); +int gmi_target_download(mi_h *h); + +/* Allocation functions: */ +void *mi_calloc(size_t count, size_t sz); +void *mi_calloc1(size_t sz); +char *mi_malloc(size_t sz); +mi_results *mi_alloc_results(void); +mi_output *mi_alloc_output(void); +mi_frames *mi_alloc_frames(void); +mi_gvar *mi_alloc_gvar(void); +mi_gvar_chg *mi_alloc_gvar_chg(void); +mi_bkpt *mi_alloc_bkpt(void); +mi_wp *mi_alloc_wp(void); +mi_stop *mi_alloc_stop(void); +mi_asm_insns *mi_alloc_asm_insns(void); +mi_asm_insn *mi_alloc_asm_insn(void); +mi_chg_reg *mi_alloc_chg_reg(void); +void mi_free_output(mi_output *r); +void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r); +void mi_free_frames(mi_frames *f); +void mi_free_aux_term(mi_aux_term *t); +void mi_free_results(mi_results *r); +void mi_free_results_but(mi_results *r, mi_results *no); +void mi_free_gvar(mi_gvar *v); +void mi_free_gvar_chg(mi_gvar_chg *p); +void mi_free_wp(mi_wp *wp); +void mi_free_stop(mi_stop *s); +void mi_free_asm_insns(mi_asm_insns *i); +void mi_free_asm_insn(mi_asm_insn *i); +void mi_free_charp_list(char **l); +void mi_free_chg_reg(mi_chg_reg *r); + +/* Porgram control: */ +/* Specify the executable and arguments for local debug. */ +int gmi_set_exec(mi_h *h, const char *file, const char *args); +/* Start running the executable. Remote sessions starts running. */ +int gmi_exec_run(mi_h *h); +/* Continue the execution after a "stop". */ +int gmi_exec_continue(mi_h *h); +/* Indicate which terminal will use the target program. For local sessions. */ +int gmi_target_terminal(mi_h *h, const char *tty_name); +/* Specify what's the local copy that have debug info. For remote sessions. */ +int gmi_file_symbol_file(mi_h *h, const char *file); +/* Continue until function return, the return value is included in the async + response. */ +int gmi_exec_finish(mi_h *h); +/* Stop the program using SIGINT. */ +int gmi_exec_interrupt(mi_h *h); +/* Next line of code. */ +int gmi_exec_next(mi_h *h); +/* Next count lines of code. */ +int gmi_exec_next_cnt(mi_h *h, int count); +/* Next line of assembler code. */ +int gmi_exec_next_instruction(mi_h *h); +/* Next line of code. Get inside functions. */ +int gmi_exec_step(mi_h *h); +/* Next count lines of code. Get inside functions. */ +int gmi_exec_step_cnt(mi_h *h, int count); +/* Next line of assembler code. Get inside calls. */ +int gmi_exec_step_instruction(mi_h *h); +/* Execute until location is reached. If file is NULL then is until next line. */ +int gmi_exec_until(mi_h *h, const char *file, int line); +int gmi_exec_until_addr(mi_h *h, void *addr); +/* Return to previous frame inmediatly. */ +mi_frames *gmi_exec_return(mi_h *h); +/* Just kill the program. Please read the notes in prg_control.c. */ +int gmi_exec_kill(mi_h *h); + +/* Target manipulation: */ +/* Connect to a remote gdbserver using the specified methode. */ +int gmi_target_select(mi_h *h, const char *type, const char *params); +/* Attach to an already running process. */ +mi_frames *gmi_target_attach(mi_h *h, pid_t pid); +/* Detach from an attached process. */ +int gmi_target_detach(mi_h *h); + +/* Miscellaneous commands: */ +/* Exit gdb killing the child is it is running. */ +void gmi_gdb_exit(mi_h *h); +/* Send the version to the console. */ +int gmi_gdb_version(mi_h *h); +/* Set a gdb variable. */ +int gmi_gdb_set(mi_h *h, const char *var, const char *val); +/* Get a gdb variable. */ +char *gmi_gdb_show(mi_h *h, const char *var); + +/* Breakpoints manipulation: */ +/* Insert a breakpoint at file:line. */ +mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line); +/* Insert a breakpoint, all available options. */ +mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, + const char *cond, int count, int thread, + const char *where); +mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line, + int temporary, int hard_assist, + const char *cond, int count, int thread); +/* Remove a breakpoint. */ +int gmi_break_delete(mi_h *h, int number); +/* Free the memory used for a breakpoint description. */ +void mi_free_bkpt(mi_bkpt *b); +/* Modify the "ignore" count for a breakpoint. */ +int gmi_break_set_times(mi_h *h, int number, int count); +/* Associate a condition with the breakpoint. */ +int gmi_break_set_condition(mi_h *h, int number, const char *condition); +/* Enable or disable a breakpoint. */ +int gmi_break_state(mi_h *h, int number, int enable); +/* Set a watchpoint. It doesn't work for remote targets! */ +mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp); + +/* Data Manipulation. */ +/* Evaluate an expression. Returns a parsed tree. */ +char *gmi_data_evaluate_expression(mi_h *h, const char *expression); +/* Path for sources. */ +int gmi_dir(mi_h *h, const char *path); +/* A very limited "data read memory" implementation. */ +int gmi_read_memory(mi_h *h, const char *exp, unsigned size, + unsigned char *dest, int *na, int convAddr, + unsigned long *addr); +mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, + const char *end, int mode); +mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, + int lines, int mode); +mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many); +int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l); +mi_chg_reg *gmi_data_list_changed_registers(mi_h *h); +int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l); +mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many); + +/* Stack manipulation. */ +/* List of frames. Arguments aren't filled. */ +mi_frames *gmi_stack_list_frames(mi_h *h); +/* List of frames. Indicating a range. */ +mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to); +/* List arguments. Only level and args filled. */ +mi_frames *gmi_stack_list_arguments(mi_h *h, int show); +/* List arguments. Indicating a range. Only level and args filled. */ +mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to); +/* Information about the current frame, including args. */ +mi_frames *gmi_stack_info_frame(mi_h *h); +/* Stack info depth. error => -1 */ +int gmi_stack_info_depth_get(mi_h *h); +/* Set stack info depth. error => -1 */ +int gmi_stack_info_depth(mi_h *h, int max_depth); +/* Change current frame. */ +int gmi_stack_select_frame(mi_h *h, int framenum); +/* List of local vars. */ +mi_results *gmi_stack_list_locals(mi_h *h, int show); + +/* Thread. */ +/* List available thread ids. */ +int gmi_thread_list_ids(mi_h *h, int **list); +/* Select a thread. */ +mi_frames *gmi_thread_select(mi_h *h, int id); +/* List available threads. */ +mi_frames *gmi_thread_list_all_threads(mi_h *h); + +/* Variable objects. */ +/* Create a variable object. */ +mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp); +mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp); +/* Create the variable and also fill the lang and attr fields. */ +mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp); +/* Delete a variable object. Doesn't free the mi_gvar data. */ +int gmi_var_delete(mi_h *h, mi_gvar *var); +/* Set the format used to represent the result. */ +int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format); +/* Fill the format field with info from gdb. */ +int gmi_var_show_format(mi_h *h, mi_gvar *var); +/* Fill the numchild field with info from gdb. */ +int gmi_var_info_num_children(mi_h *h, mi_gvar *var); +/* Fill the type field with info from gdb. */ +int gmi_var_info_type(mi_h *h, mi_gvar *var); +/* Fill the expression and lang fields with info from gdb. + Note that lang isn't filled during creation. */ +int gmi_var_info_expression(mi_h *h, mi_gvar *var); +/* Fill the attr field with info from gdb. + Note that attr isn't filled during creation. */ +int gmi_var_show_attributes(mi_h *h, mi_gvar *var); +/* Update variable. Use NULL for all. + Note that *changed can be NULL if none updated. */ +int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed); +/* Change variable. Fills the value field. */ +int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression); +/* Get current value for a variable. */ +int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var); +/* List children. It ONLY returns the first level information. :-( */ +int gmi_var_list_children(mi_h *h, mi_gvar *var); + +#ifdef __cplusplus +}; + +/* C++ interface */ + +/* + State Can: + disconnected Connect + connected SelectTarget, Disconnect + target_specified TargetUnselect, Run, Set breakpoints/watchpoints, etc. + running Stop + stopped Kill, Restart?, Step, Trace, Continue, etc. + [auto exit] + + Modes: + dmX11 Local debug for X11. + dmLinux Local debug for Linux console. + dmRemote Remote debug. +*/ +class MIDebugger +{ +public: + MIDebugger(); + ~MIDebugger(); + + enum eState { disconnected, connected, target_specified, running, stopped }; + enum dMode { dmX11, dmLinux, dmRemote, dmPID }; + enum endianType { enUnknown, enLittle, enBig }; + // Currently tested architectures + enum archType { arUnknown, arIA32, arSPARC, arPIC14, arAVR, arUnsupported }; + + int Connect(bool remote=false); /* remote is currently ignored. */ + int Disconnect(); + /* SelectTarget* */ + int SelectTargetX11(const char *exec, const char *args=NULL, + const char *auxtty=NULL); + int SelectTargetLinux(const char *exec, const char *args, + const char *auxtty=NULL); + int SelectTargetRemote(const char *exec, const char *rparams, + const char *rtype=NULL, bool download=false); + // TODO: Linux PIDs can be represented as intergers. What should I use? + // ato_pid_t doesn't exist ;-) + mi_frames *SelectTargetPID(const char *exec, int pid); + int TargetUnselect(); + int Run(); + int Stop(); + int Poll(mi_stop *&rs); + int Continue(); + int RunOrContinue(); + int Kill(); + mi_bkpt *Breakpoint(const char *file, int line); + mi_bkpt *Breakpoint(const char *where, bool temporary=false, const char *cond=NULL, + int count=-1, int thread=-1, bool hard_assist=false); + mi_bkpt *BreakpointFull(const char *file, int line, bool temporary=false, + const char *cond=NULL, int count=-1, int thread=-1, + bool hard_assist=false); + mi_bkpt *Breakpoint(mi_bkpt *b); + int BreakDelete(mi_bkpt *b); + int BreakAfter(mi_bkpt *b) + { + if (state!=target_specified && state!=stopped) + return 0; + return gmi_break_set_times(h,b->number,b->ignore); + } + mi_wp *Watchpoint(enum mi_wp_mode mode, const char *exp); + int WatchDelete(mi_wp *w); + int RunToMain(); + int StepOver(bool inst=false); + int TraceInto(bool inst=false); + int GoTo(const char *file, int line); + int GoTo(void *addr); + int FinishFun(); + mi_frames *ReturnNow(); + mi_frames *CallStack(bool args); + char *EvalExpression(const char *exp); + char *ModifyExpression(char *exp, char *newVal); + mi_gvar *AddgVar(const char *exp, int frame=-1) + { + if (state!=stopped) + return NULL; + return gmi_full_var_create(h,frame,exp); + } + int DelgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_delete(h,var); + } + int EvalgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_evaluate_expression(h,var); + } + int GetChildgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_list_children(h,var); + } + int FillTypeVal(mi_gvar *var); + int FillOneTypeVal(mi_gvar *var); + int FillAttr(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_show_attributes(h,var); + } + int FillFormat(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_show_format(h,var); + } + int SetFormatgVar(mi_gvar *var, enum mi_gvar_fmt format) + { + if (state!=stopped) + return 0; + return gmi_var_set_format(h,var,format); + } + int ListChangedgVar(mi_gvar_chg *&changed) + { + if (state!=stopped) + return 0; + return gmi_var_update(h,NULL,&changed); + } + int AssigngVar(mi_gvar *var, const char *exp); + int Send(const char *command); + int Version() + { + if (state==running || state==disconnected) + return 0; + return gmi_gdb_version(h); + } + int PathSources(const char *path) + { + if (state==running || state==disconnected) + return 0; + return gmi_dir(h,path); + } + int ReadMemory(const char *exp, unsigned size, unsigned char *dest, + int &na, int convAddr, unsigned long *addr) + { + if (state!=stopped) + return 0; + return gmi_read_memory(h,exp,size,dest,&na,convAddr,addr); + } + char *Show(const char *var); + int ThreadListIDs(int *&list) + { + if (state!=stopped) + return 0; + return gmi_thread_list_ids(h,&list); + } + mi_frames *ThreadList() + { + if (state!=stopped) + return 0; + return gmi_thread_list_all_threads(h); + } + mi_frames *ThreadSelect(int id) + { + if (state!=stopped) + return NULL; + return gmi_thread_select(h,id); + } + mi_asm_insns *Disassemble(const char *start, const char *end, int mode) + { + if (state!=stopped) + return NULL; + return gmi_data_disassemble_se(h,start,end,mode); + } + mi_asm_insns *Disassemble(const char *file, int line, int lines, int mode) + { + if (state!=stopped) + return NULL; + return gmi_data_disassemble_fl(h,file,line,lines,mode); + } + mi_chg_reg *GetRegisterNames(int *how_many) + { + if (state!=target_specified && state!=stopped) + return NULL; + return gmi_data_list_register_names(h,how_many); + } + int GetRegisterNames(mi_chg_reg *chg) + { + if (state!=target_specified && state!=stopped) + return 0; + return gmi_data_list_register_names_l(h,chg); + } + int GetRegisterValues(mi_chg_reg *chg) + { + if (state!=stopped) + return 0; + return gmi_data_list_register_values(h,fm_natural,chg); + } + mi_chg_reg *GetRegisterValues(int *how_many) + { + if (state!=stopped) + return 0; + return gmi_data_list_all_register_values(h,fm_natural,how_many); + } + mi_chg_reg *GetChangedRegisters() + { + if (state!=stopped) + return NULL; + mi_chg_reg *chg=gmi_data_list_changed_registers(h); + if (chg && !gmi_data_list_register_values(h,fm_natural,chg)) + { + mi_free_chg_reg(chg); + chg=NULL; + } + return chg; + } + int UpdateRegisters(mi_chg_reg *regs); + + endianType GetTargetEndian(); + archType GetTargetArchitecture(); + eState GetState() { return state; } + + /* Some wrappers */ + static void SetGDBExe(const char *name) { mi_set_gdb_exe(name); } + static const char *GetGDBExe() { return mi_get_gdb_exe(); } + static void SetXTermExe(const char *name) { mi_set_xterm_exe(name); } + static const char *GetXTermExe() { return mi_get_xterm_exe(); } + static void SetGDBStartFile(const char *name) { mi_set_gdb_start(name); } + static const char *GetGDBStartFile() { return mi_get_gdb_start(); } + static void SetGDBConnFile(const char *name) { mi_set_gdb_conn(name); } + static const char *GetGDBConnFile() { return mi_get_gdb_conn(); } + static void SetMainFunc(const char *name) { mi_set_main_func(name); } + static const char *GetMainFunc() { return mi_get_main_func(); } + + static const char *GetErrorStr() { return mi_get_error_str(); } + static const char *GetGDBError() { return mi_error_from_gdb; } + static int GetErrorNumber() { return mi_error; } + int GetErrorNumberSt(); + void SetConsoleCB(stream_cb cb, void *data=NULL) + { mi_set_console_cb(h,cb,data); } + void SetTargetCB(stream_cb cb, void *data=NULL) + { mi_set_target_cb(h,cb,data); } + void SetLogCB(stream_cb cb, void *data=NULL) + { mi_set_log_cb(h,cb,data); } + void SetAsyncCB(async_cb cb, void *data=NULL) + { mi_set_async_cb(h,cb,data); } + void SetToGDBCB(stream_cb cb, void *data=NULL) + { mi_set_to_gdb_cb(h,cb,data); } + void SetFromGDBCB(stream_cb cb, void *data=NULL) + { mi_set_from_gdb_cb(h,cb,data); } + void SetTimeOutCB(tm_cb cb, void *data) + { mi_set_time_out_cb(h,cb,data); } + void SetTimeOut(int to) + { mi_set_time_out(h,to); } + void ForceMIVersion(unsigned vMajor, unsigned vMiddle, unsigned vMinor) + { mi_force_version(h,vMajor,vMiddle,vMinor); } + + const char *GetAuxTTY() + { return aux_tty ? aux_tty->tty : NULL; } + +protected: + eState state; + dMode mode; + endianType targetEndian; + archType targetArch; + bool preRun; // Remote targets starts running but outside main. + mi_h *h; + mi_aux_term *aux_tty; + int waitingTempBkpt; + + int SelectTargetTTY(const char *exec, const char *args, const char *auxtty, + dMode m); +}; + +#endif + diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..5440ab0 --- /dev/null +++ b/src/misc.c @@ -0,0 +1,118 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Miscellaneous commands. + Comments: + GDB/MI commands for the "Miscellaneous Commands" section.@p + +@
    +gdb command:       Implemented?
    +
    +-gdb-exit          Yes
    +-gdb-set           Yes
    +-gdb-show          Yes
    +-gdb-version       Yes
    +@
    + +GDB Bug workaround for "-gdb-show architecture": gdb 6.1 and olders doesn't +report it in "value", but they give the output of "show architecture". In +6.4 we observed that not even a clue is reported. So now we always use +"show architecture". + +***************************************************************************/ + +#include +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_gdb_exit(mi_h *h) +{ + mi_send(h,"-gdb-exit\n"); +} + +void mi_gdb_version(mi_h *h) +{ + mi_send(h,"-gdb-version\n"); +} + +void mi_gdb_set(mi_h *h, const char *var, const char *val) +{ + mi_send(h,"-gdb-set %s %s\n",var,val); +} + +void mi_gdb_show(mi_h *h, const char *var) +{ + if (strcmp(var,"architecture")==0) + mi_send(h,"show %s\n",var); + else + mi_send(h,"-gdb-show %s\n",var); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Exit gdb killing the child is it is running. + + Command: -gdb-exit + +***************************************************************************/ + +void gmi_gdb_exit(mi_h *h) +{ + mi_gdb_exit(h); + mi_res_simple_exit(h); +} + +/**[txh]******************************************************************** + + Description: + Send the version to the console. + + Command: -gdb-version + Return: !=0 OK + +***************************************************************************/ + +int gmi_gdb_version(mi_h *h) +{ + mi_gdb_version(h); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Set a gdb variable. + + Command: -gdb-set + Return: !=0 OK + +***************************************************************************/ + +int gmi_gdb_set(mi_h *h, const char *var, const char *val) +{ + mi_gdb_set(h,var,val); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Get a gdb variable. + + Command: -gdb-show + Return: The current value of the variable or NULL on error. + +***************************************************************************/ + +char *gmi_gdb_show(mi_h *h, const char *var) +{ + mi_gdb_show(h,var); + return mi_res_value(h); +} + diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..0dea6cb --- /dev/null +++ b/src/parse.c @@ -0,0 +1,1923 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2007 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Parser. + Comments: + Parses the output of gdb. It basically converts the text from gdb into a +tree (could be a complex one) that we can easily interpret using C code. + +***************************************************************************/ + +#include +#include +#include +#include "mi_gdb.h" + +mi_results *mi_get_result(const char *str, const char **end); +int mi_get_value(mi_results *r, const char *str, const char **end); + + +/* GDB BUG!!!! I got: +^error,msg="Problem parsing arguments: data-evaluate-expression ""1+2""" +Afects gdb 2002-04-01-cvs and 6.1.1 for sure. +That's an heuristical workaround. +*/ +static inline +int EndOfStr(const char *s) +{ + if (*s=='"') + { + s++; + return !*s || *s==',' || *s==']' || *s=='}'; + } + return 0; +} + +int mi_get_cstring_r(mi_results *r, const char *str, const char **end) +{ + const char *s; + char *d; + int len; + + if (*str!='"') + { + mi_error=MI_PARSER; + return 0; + } + str++; + /* Meassure. */ + for (s=str, len=0; *s && !EndOfStr(s); s++) + { + if (*s=='\\') + { + if (!*s) + { + mi_error=MI_PARSER; + return 0; + } + s++; + } + len++; + } + /* Copy. */ + r->type=t_const; + d=r->v.cstr=mi_malloc(len+1); + if (!r->v.cstr) + return 0; + for (s=str; *s && !EndOfStr(s); s++, d++) + { + if (*s=='\\') + { + s++; + switch (*s) + { + case 'n': + *d='\n'; + break; + case 't': + *d='\t'; + break; + default: + *d=*s; + } + } + else + *d=*s; + } + *d=0; + if (end) + *end=s+1; + + return 1; +} + +/* TODO: What's a valid variable name? + I'll assume a-zA-Z0-9_- */ +inline +int mi_is_var_name_char(char c) +{ + return isalnum(c) || c=='-' || c=='_'; +} + +char *mi_get_var_name(const char *str, const char **end) +{ + const char *s; + char *r; + int l; + /* Meassure. */ + for (s=str; *s && mi_is_var_name_char(*s); s++); + if (*s!='=') + { + mi_error=MI_PARSER; + return NULL; + } + /* Allocate. */ + l=s-str; + r=mi_malloc(l+1); + /* Copy. */ + memcpy(r,str,l); + r[l]=0; + if (end) + *end=s+1; + return r; +} + + +int mi_get_list_res(mi_results *r, const char *str, const char **end, char closeC) +{ + mi_results *last_r, *rs; + + last_r=NULL; + do + { + rs=mi_get_result(str,&str); + if (last_r) + last_r->next=rs; + else + r->v.rs=rs; + last_r=rs; + if (*str==closeC) + { + *end=str+1; + return 1; + } + if (*str!=',') + break; + str++; + } + while (1); + + mi_error=MI_PARSER; + return 0; +} + +#ifdef __APPLE__ +int mi_get_tuple_val(mi_results *r, const char *str, const char **end) +{ + mi_results *last_r, *rs; + + last_r=NULL; + do + { + rs=mi_alloc_results(); + if (!rs || !mi_get_value(rs,str,&str)) + { + mi_free_results(rs); + return 0; + } + /* Note that rs->var is NULL, that indicates that's just a value and not + a result. */ + if (last_r) + last_r->next=rs; + else + r->v.rs=rs; + last_r=rs; + if (*str=='}') + { + *end=str+1; + return 1; + } + if (*str!=',') + break; + str++; + } + while (1); + + mi_error=MI_PARSER; + return 0; +} +#endif /* __APPLE__ */ + +int mi_get_tuple(mi_results *r, const char *str, const char **end) +{ + if (*str!='{') + { + mi_error=MI_PARSER; + return 0; + } + r->type=t_tuple; + str++; + if (*str=='}') + {/* Special case: empty tuple */ + *end=str+1; + return 1; + } + #ifdef __APPLE__ + if (mi_is_var_name_char(*str)) + return mi_get_list_res(r,str,end,'}'); + return mi_get_tuple_val(r,str,end); + #else /* __APPLE__ */ + return mi_get_list_res(r,str,end,'}'); + #endif /* __APPLE__ */ +} + +int mi_get_list_val(mi_results *r, const char *str, const char **end) +{ + mi_results *last_r, *rs; + + last_r=NULL; + do + { + rs=mi_alloc_results(); + if (!rs || !mi_get_value(rs,str,&str)) + { + mi_free_results(rs); + return 0; + } + /* Note that rs->var is NULL, that indicates that's just a value and not + a result. */ + if (last_r) + last_r->next=rs; + else + r->v.rs=rs; + last_r=rs; + if (*str==']') + { + *end=str+1; + return 1; + } + if (*str!=',') + break; + str++; + } + while (1); + + mi_error=MI_PARSER; + return 0; +} + +int mi_get_list(mi_results *r, const char *str, const char **end) +{ + if (*str!='[') + { + mi_error=MI_PARSER; + return 0; + } + r->type=t_list; + str++; + if (*str==']') + {/* Special case: empty list */ + *end=str+1; + return 1; + } + /* Comment: I think they could choose () for values. Is confusing in this way. */ + if (mi_is_var_name_char(*str)) + return mi_get_list_res(r,str,end,']'); + return mi_get_list_val(r,str,end); +} + +int mi_get_value(mi_results *r, const char *str, const char **end) +{ + switch (str[0]) + { + case '"': + return mi_get_cstring_r(r,str,end); + case '{': + return mi_get_tuple(r,str,end); + case '[': + return mi_get_list(r,str,end); + } + mi_error=MI_PARSER; + return 0; +} + +mi_results *mi_get_result(const char *str, const char **end) +{ + char *var; + mi_results *r; + + var=mi_get_var_name(str,&str); + if (!var) + return NULL; + + r=mi_alloc_results(); + if (!r) + { + free(var); + return NULL; + } + r->var=var; + + if (!mi_get_value(r,str,end)) + { + mi_free_results(r); + return NULL; + } + + return r; +} + +mi_output *mi_get_results_alone(mi_output *r,const char *str) +{ + mi_results *last_r, *rs; + + /* * results */ + last_r=NULL; + do + { + if (!*str) + return r; + if (*str!=',') + { + mi_error=MI_PARSER; + break; + } + str++; + rs=mi_get_result(str,&str); + if (!rs) + break; + if (!last_r) + r->c=rs; + else + last_r->next=rs; + last_r=rs; + } + while (1); + mi_free_output(r); + return NULL; +} + +mi_output *mi_parse_result_record(mi_output *r,const char *str) +{ + r->type=MI_T_RESULT_RECORD; + + /* Solve the result-class. */ + if (strncmp(str,"done",4)==0) + { + str+=4; + r->tclass=MI_CL_DONE; + } + else if (strncmp(str,"running",7)==0) + { + str+=7; + r->tclass=MI_CL_RUNNING; + } + else if (strncmp(str,"connected",9)==0) + { + str+=9; + r->tclass=MI_CL_CONNECTED; + } + else if (strncmp(str,"error",5)==0) + { + str+=5; + r->tclass=MI_CL_ERROR; + } + else if (strncmp(str,"exit",4)==0) + { + str+=4; + r->tclass=MI_CL_EXIT; + } + else + { + mi_error=MI_UNKNOWN_RESULT; + return NULL; + } + + return mi_get_results_alone(r,str); +} + +mi_output *mi_parse_asyn(mi_output *r,const char *str) +{ + r->type=MI_T_OUT_OF_BAND; + r->stype=MI_ST_ASYNC; + /* async-class. */ + if (strncmp(str,"stopped",7)==0) + { + r->tclass=MI_CL_STOPPED; + str+=7; + return mi_get_results_alone(r,str); + } + if (strncmp(str,"download",8)==0) + { + r->tclass=MI_CL_DOWNLOAD; + str+=8; + return mi_get_results_alone(r,str); + } + mi_error=MI_UNKNOWN_ASYNC; + mi_free_output(r); + return NULL; +} + +mi_output *mi_parse_exec_asyn(mi_output *r,const char *str) +{ + r->sstype=MI_SST_EXEC; + return mi_parse_asyn(r,str); +} + +mi_output *mi_parse_status_asyn(mi_output *r,const char *str) +{ + r->sstype=MI_SST_STATUS; + return mi_parse_asyn(r,str); +} + +mi_output *mi_parse_notify_asyn(mi_output *r,const char *str) +{ + r->sstype=MI_SST_NOTIFY; + return mi_parse_asyn(r,str); +} + +mi_output *mi_console(mi_output *r,const char *str) +{ + r->type=MI_T_OUT_OF_BAND; + r->stype=MI_ST_STREAM; + r->c=mi_alloc_results(); + if (!r->c || !mi_get_cstring_r(r->c,str,NULL)) + { + mi_free_output(r); + return NULL; + } + return r; +} + +mi_output *mi_console_stream(mi_output *r,const char *str) +{ + r->sstype=MI_SST_CONSOLE; + return mi_console(r,str); +} + +mi_output *mi_target_stream(mi_output *r,const char *str) +{ + r->sstype=MI_SST_TARGET; + return mi_console(r,str); +} + +mi_output *mi_log_stream(mi_output *r,const char *str) +{ + r->sstype=MI_SST_LOG; + return mi_console(r,str); +} + +mi_output *mi_parse_gdb_output(const char *str) +{ + char type=str[0]; + + mi_output *r=mi_alloc_output(); + if (!r) + { + mi_error=MI_OUT_OF_MEMORY; + return NULL; + } + str++; + switch (type) + { + case '^': + return mi_parse_result_record(r,str); + case '*': + return mi_parse_exec_asyn(r,str); + case '+': + return mi_parse_status_asyn(r,str); + case '=': + return mi_parse_notify_asyn(r,str); + case '~': + return mi_console_stream(r,str); + case '@': + return mi_target_stream(r,str); + case '&': + return mi_log_stream(r,str); + } + mi_error=MI_PARSER; + return NULL; +} + +mi_output *mi_get_rrecord(mi_output *r) +{ + if (!r) + return NULL; + while (r) + { + if (r->type==MI_T_RESULT_RECORD) + return r; + r=r->next; + } + return r; +} + +mi_results *mi_get_var_r(mi_results *r, const char *var) +{ + while (r) + { + if (strcmp(r->var,var)==0) + return r; + r=r->next; + } + return NULL; +} + +mi_results *mi_get_var(mi_output *res, const char *var) +{ + if (!res) + return NULL; + return mi_get_var_r(res->c,var); +} + +int mi_get_async_stop_reason(mi_output *r, char **reason) +{ + int found_stopped=0; + + *reason=NULL; + while (r) + { + if (r->type==MI_T_RESULT_RECORD && r->tclass==MI_CL_ERROR) + { + if (r->c->type==t_const) + *reason=r->c->v.cstr; + return 0; + } + if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && + r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) + { + mi_results *p=r->c; + found_stopped=1; + while (p) + { + if (strcmp(p->var,"reason")==0) + { + *reason=p->v.cstr; + return 1; + } + p=p->next; + } + } + r=r->next; + } + if (*reason==NULL && found_stopped) + { + *reason=strdup("unknown (temp bkpt?)"); + return 1; + } + return 0; +} + +mi_frames *mi_get_async_frame(mi_output *r) +{ + while (r) + { + if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && + r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) + { + mi_results *p=r->c; + while (p) + { + if (strcmp(p->var,"frame")==0) + return mi_parse_frame(p->v.rs); + p=p->next; + } + } + r=r->next; + } + return NULL; +} + +int mi_res_simple(mi_h *h, int tclass, int accert_ret) +{ + mi_output *r, *res; + int ret=0; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + + if (res) + ret=res->tclass==tclass; + mi_free_output(r); + + return ret; +} + + +int mi_res_simple_done(mi_h *h) +{ + return mi_res_simple(h,MI_CL_DONE,0); +} + +int mi_res_simple_exit(mi_h *h) +{ + return mi_res_simple(h,MI_CL_EXIT,1); +} + +int mi_res_simple_running(mi_h *h) +{ + return mi_res_simple(h,MI_CL_RUNNING,0); +} + +int mi_res_simple_connected(mi_h *h) +{ + return mi_res_simple(h,MI_CL_CONNECTED,0); +} + +mi_results *mi_res_var(mi_h *h, const char *var, int tclass) +{ + mi_output *r, *res; + mi_results *the_var=NULL; + + r=mi_get_response_blk(h); + /* All the code that follows is "NULL" tolerant. */ + /* Look for the result-record. */ + res=mi_get_rrecord(r); + /* Look for the desired var. */ + if (res && res->tclass==tclass) + the_var=mi_get_var(res,var); + /* Release all but the one we want. */ + mi_free_output_but(r,NULL,the_var); + return the_var; +} + +mi_results *mi_res_done_var(mi_h *h, const char *var) +{ + return mi_res_var(h,var,MI_CL_DONE); +} + +mi_frames *mi_parse_frame(mi_results *c) +{ + mi_frames *res=mi_alloc_frames(); + char *end; + + if (res) + { + while (c) + { + if (c->type==t_const) + { + if (strcmp(c->var,"level")==0) + res->level=atoi(c->v.cstr); + else if (strcmp(c->var,"addr")==0) + res->addr=(void *)strtoul(c->v.cstr,&end,0); + else if (strcmp(c->var,"func")==0) + { + res->func=c->v.cstr; + c->v.cstr=NULL; + } + else if (strcmp(c->var,"file")==0) + { + res->file=c->v.cstr; + c->v.cstr=NULL; + } + else if (strcmp(c->var,"from")==0) + { + res->from=c->v.cstr; + c->v.cstr=NULL; + } + else if (strcmp(c->var,"line")==0) + res->line=atoi(c->v.cstr); + } + else if (c->type==t_list && strcmp(c->var,"args")==0) + { + res->args=c->v.rs; + c->v.rs=NULL; + } + c=c->next; + } + } + return res; +} + +mi_frames *mi_res_frame(mi_h *h) +{ + mi_results *r=mi_res_done_var(h,"frame"); + mi_frames *f=NULL; + + if (r && r->type==t_tuple) + f=mi_parse_frame(r->v.rs); + mi_free_results(r); + return f; +} + +mi_frames *mi_res_frames_array(mi_h *h, const char *var) +{ + mi_results *r=mi_res_done_var(h,var), *c; + mi_frames *res=NULL, *nframe, *last=NULL; + + if (!r) + return NULL; +#ifdef __APPLE__ + if (r->type!=t_list && r->type!=t_tuple) +#else + if (r->type!=t_list) +#endif + { + mi_free_results(r); + return NULL; + } + c=r->v.rs; + while (c) + { + if (strcmp(c->var,"frame")==0 && c->type==t_tuple) + { + nframe=mi_parse_frame(c->v.rs); + if (nframe) + { + if (!last) + res=nframe; + else + last->next=nframe; + last=nframe; + } + } + c=c->next; + } + mi_free_results(r); + return res; +} + +mi_frames *mi_res_frames_list(mi_h *h) +{ + mi_output *r, *res; + mi_frames *ret=NULL, *nframe, *last=NULL; + mi_results *c; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + if (res && res->tclass==MI_CL_DONE) + { + c=res->c; + while (c) + { + if (strcmp(c->var,"frame")==0 && c->type==t_tuple) + { + nframe=mi_parse_frame(c->v.rs); + if (nframe) + { + if (!last) + ret=nframe; + else + last->next=nframe; + last=nframe; + } + } + c=c->next; + } + } + mi_free_output(r); + return ret; +} + +int mi_get_thread_ids(mi_output *res, int **list) +{ + mi_results *vids, *lids; + int ids=-1, i; + + *list=NULL; + vids=mi_get_var(res,"number-of-threads"); + lids=mi_get_var(res,"thread-ids"); + if (vids && vids->type==t_const && + lids && lids->type==t_tuple) + { + ids=atoi(vids->v.cstr); + if (ids) + { + int *lst; + lst=(int *)mi_calloc(ids,sizeof(int)); + if (lst) + { + lids=lids->v.rs; + i=0; + while (lids) + { + if (strcmp(lids->var,"thread-id")==0 && lids->type==t_const) + lst[i++]=atoi(lids->v.cstr); + lids=lids->next; + } + *list=lst; + } + else + ids=-1; + } + } + return ids; +} + +int mi_res_thread_ids(mi_h *h, int **list) +{ + mi_output *r, *res; + int ids=-1; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + if (res && res->tclass==MI_CL_DONE) + ids=mi_get_thread_ids(res,list); + mi_free_output(r); + return ids; +} + +enum mi_gvar_lang mi_lang_str_to_enum(const char *lang) +{ + enum mi_gvar_lang lg=lg_unknown; + + if (strcmp(lang,"C")==0) + lg=lg_c; + else if (strcmp(lang,"C++")==0) + lg=lg_cpp; + else if (strcmp(lang,"Java")==0) + lg=lg_java; + + return lg; +} + +const char *mi_lang_enum_to_str(enum mi_gvar_lang lang) +{ + const char *lg; + + switch (lang) + { + case lg_c: + lg="C"; + break; + case lg_cpp: + lg="C++"; + break; + case lg_java: + lg="Java"; + break; + /*case lg_unknown:*/ + default: + lg="unknown"; + break; + } + return lg; +} + +enum mi_gvar_fmt mi_format_str_to_enum(const char *format) +{ + enum mi_gvar_fmt fmt=fm_natural; + + if (strcmp(format,"binary")==0) + fmt=fm_binary; + else if (strcmp(format,"decimal")==0) + fmt=fm_decimal; + else if (strcmp(format,"hexadecimal")==0) + fmt=fm_hexadecimal; + else if (strcmp(format,"octal")==0) + fmt=fm_octal; + + return fmt; +} + +const char *mi_format_enum_to_str(enum mi_gvar_fmt format) +{ + const char *fmt; + + switch (format) + { + case fm_natural: + fmt="natural"; + break; + case fm_binary: + fmt="binary"; + break; + case fm_decimal: + fmt="decimal"; + break; + case fm_hexadecimal: + fmt="hexadecimal"; + break; + case fm_octal: + fmt="octal"; + break; + case fm_raw: + fmt="raw"; + break; + default: + fmt="unknown"; + } + return fmt; +} + +char mi_format_enum_to_char(enum mi_gvar_fmt format) +{ + char fmt; + + switch (format) + { + case fm_natural: + fmt='N'; + break; + case fm_binary: + fmt='t'; + break; + case fm_decimal: + fmt='d'; + break; + case fm_hexadecimal: + fmt='x'; + break; + case fm_octal: + fmt='o'; + break; + case fm_raw: + fmt='r'; + break; + default: + fmt=' '; + } + return fmt; +} + +mi_gvar *mi_get_gvar(mi_output *o, mi_gvar *cur, const char *expression) +{ + mi_results *r; + mi_gvar *res=cur ? cur : mi_alloc_gvar(); + int l; + + if (!res) + return res; + r=o->c; + if (expression) + res->exp=strdup(expression); + while (r) + { + if (r->type==t_const) + { + if (strcmp(r->var,"name")==0) + { + free(res->name); + res->name=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"numchild")==0) + { + res->numchild=atoi(r->v.cstr); + } + else if (strcmp(r->var,"type")==0) + { + free(res->type); + res->type=r->v.cstr; + r->v.cstr=NULL; + l=strlen(res->type); + if (l && res->type[l-1]=='*') + res->ispointer=1; + } + else if (strcmp(r->var,"lang")==0) + { + res->lang=mi_lang_str_to_enum(r->v.cstr); + } + else if (strcmp(r->var,"exp")==0) + { + free(res->exp); + res->exp=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"format")==0) + { + res->format=mi_format_str_to_enum(r->v.cstr); + } + else if (strcmp(r->var,"attr")==0) + { /* Note: gdb 6.1.1 have only this: */ + if (strcmp(r->v.cstr,"editable")==0) + res->attr=MI_ATTR_EDITABLE; + else /* noneditable */ + res->attr=MI_ATTR_NONEDITABLE; + } + } + r=r->next; + } + return res; +} + +mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression) +{ + mi_output *r, *res; + mi_gvar *gvar=NULL; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + if (res && res->tclass==MI_CL_DONE) + gvar=mi_get_gvar(res,cur,expression); + mi_free_output(r); + return gvar; +} + +mi_gvar_chg *mi_get_gvar_chg(mi_results *r) +{ + mi_gvar_chg *n; + + if (r->type!=t_const) + return NULL; + n=mi_alloc_gvar_chg(); + if (n) + { + while (r) + { + if (r->type==t_const) + { + if (strcmp(r->var,"name")==0) + { + n->name=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"in_scope")==0) + { + n->in_scope=strcmp(r->v.cstr,"true")==0; + } + else if (strcmp(r->var,"new_type")==0) + { + n->new_type=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"new_num_children")==0) + { + n->new_num_children=atoi(r->v.cstr); + } + // type_changed="false" is the default + } + r=r->next; + } + } + return n; +} + +int mi_res_changelist(mi_h *h, mi_gvar_chg **changed) +{ + mi_gvar_chg *last, *n; + mi_results *res=mi_res_done_var(h,"changelist"), *r; + int count=0; + + *changed=NULL; + if (!res) + return 0; + last=NULL; + count=1; + n=NULL; + r=res->v.rs; + + if (res->type==t_list) + {// MI v2 a list of tuples + while (r) + { + if (r->type==t_tuple) + { + n=mi_get_gvar_chg(r->v.rs); + if (n) + { + if (last) + last->next=n; + else + *changed=n; + last=n; + count++; + } + } + r=r->next; + } + } + else if (res->type==t_tuple) + {// MI v1 a tuple with all together *8-P + while (r) + { + if (r->type==t_const) /* Just in case. */ + {/* Get one var. */ + if (strcmp(r->var,"name")==0) + { + if (n) + {/* Add to the list*/ + if (last) + last->next=n; + else + *changed=n; + last=n; + count++; + } + n=mi_alloc_gvar_chg(); + if (!n) + { + mi_free_gvar_chg(*changed); + return 0; + } + n->name=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"in_scope")==0) + { + n->in_scope=strcmp(r->v.cstr,"true")==0; + } + else if (strcmp(r->var,"new_type")==0) + { + n->new_type=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"new_num_children")==0) + { + n->new_num_children=atoi(r->v.cstr); + } + // type_changed="false" is the default + } + r=r->next; + } + if (n) + {/* Add to the list*/ + if (last) + last->next=n; + else + *changed=n; + last=n; + count++; + } + } + mi_free_results(res); + + return count; +} + +int mi_get_children(mi_results *ch, mi_gvar *v) +{ + mi_gvar *cur=NULL, *aux; + int i=0, count=v->numchild, l; + + while (ch) + { + if (strcmp(ch->var,"child")==0 && ch->type==t_tuple && iv.rs; + aux=mi_alloc_gvar(); + if (!aux) + return 0; + if (!v->child) + v->child=aux; + else + cur->next=aux; + cur=aux; + cur->parent=v; + cur->depth=v->depth+1; + + while (r) + { + if (r->type==t_const) + { + if (strcmp(r->var,"name")==0) + { + cur->name=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"exp")==0) + { + cur->exp=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"type")==0) + { + cur->type=r->v.cstr; + r->v.cstr=NULL; + l=strlen(cur->type); + if (l && cur->type[l-1]=='*') + cur->ispointer=1; + } + else if (strcmp(r->var,"value")==0) + { + cur->value=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"numchild")==0) + { + cur->numchild=atoi(r->v.cstr); + } + } + r=r->next; + } + i++; + } + ch=ch->next; + } + v->vischild=i; + v->opened=1; + return i==v->numchild; +} + +int mi_res_children(mi_h *h, mi_gvar *v) +{ + mi_output *r, *res; + int ok=0; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + if (res && res->tclass==MI_CL_DONE) + { + mi_results *num=mi_get_var(res,"numchild"); + if (num && num->type==t_const) + { + v->numchild=atoi(num->v.cstr); + if (v->child) + { + mi_free_gvar(v->child); + v->child=NULL; + } + if (v->numchild) + { + mi_results *ch =mi_get_var(res,"children"); + if (ch && ch->type!=t_const) /* MI v1 tuple, MI v2 list */ + ok=mi_get_children(ch->v.rs,v); + } + else + ok=1; + } + } + mi_free_output(r); + return ok; +} + +mi_bkpt *mi_get_bkpt(mi_results *p) +{ + mi_bkpt *res; + char *end; + + res=mi_alloc_bkpt(); + if (!res) + return NULL; + while (p) + { + if (p->type==t_const && p->var) + { + if (strcmp(p->var,"number")==0) + res->number=atoi(p->v.cstr); + else if (strcmp(p->var,"type")==0) + { + if (strcmp(p->v.cstr,"breakpoint")==0) + res->type=t_breakpoint; + else + res->type=t_unknown; + } + else if (strcmp(p->var,"disp")==0) + { + if (strcmp(p->v.cstr,"keep")==0) + res->disp=d_keep; + else if (strcmp(p->v.cstr,"del")==0) + res->disp=d_del; + else + res->disp=d_unknown; + } + else if (strcmp(p->var,"enabled")==0) + res->enabled=p->v.cstr[0]=='y'; + else if (strcmp(p->var,"addr")==0) + res->addr=(void *)strtoul(p->v.cstr,&end,0); + else if (strcmp(p->var,"func")==0) + { + res->func=p->v.cstr; + p->v.cstr=NULL; + } + else if (strcmp(p->var,"file")==0) + { + res->file=p->v.cstr; + p->v.cstr=NULL; + } + else if (strcmp(p->var,"line")==0) + res->line=atoi(p->v.cstr); + else if (strcmp(p->var,"times")==0) + res->times=atoi(p->v.cstr); + else if (strcmp(p->var,"ignore")==0) + res->ignore=atoi(p->v.cstr); + else if (strcmp(p->var,"cond")==0) + { + res->cond=p->v.cstr; + p->v.cstr=NULL; + } + } + p=p->next; + } + return res; +} + +mi_bkpt *mi_res_bkpt(mi_h *h) +{ + mi_results *r=mi_res_done_var(h,"bkpt"); + mi_bkpt *b=NULL; + + if (r && r->type==t_tuple) + b=mi_get_bkpt(r->v.rs); + mi_free_results(r); + return b; +} + +mi_wp *mi_get_wp(mi_results *p, enum mi_wp_mode m) +{ + mi_wp *res=mi_alloc_wp(); + + if (res) + { + res->mode=m; + while (p) + { + if (p->type==t_const && p->var) + { + if (strcmp(p->var,"number")==0) + { + res->number=atoi(p->v.cstr); + res->enabled=1; + } + else if (strcmp(p->var,"exp")==0) + { + res->exp=p->v.cstr; + p->v.cstr=NULL; + } + } + p=p->next; + } + } + return res; +} + +mi_wp *mi_parse_wp_res(mi_output *r) +{ + mi_results *p; + enum mi_wp_mode m=wm_unknown; + + /* The info is in a result wpt=... */ + p=r->c; + while (p) + { + if (p->var) + { + if (strcmp(p->var,"wpt")==0) + m=wm_write; + else if (strcmp(p->var,"hw-rwpt")==0) + m=wm_read; + else if (strcmp(p->var,"hw-awpt")==0) + m=wm_rw; + if (m!=wm_unknown) + break; + } + p=p->next; + } + if (!p || p->type!=t_tuple) + return NULL; + /* Scan the values inside it. */ + return mi_get_wp(p->v.rs,m); +} + +mi_wp *mi_res_wp(mi_h *h) +{ + mi_output *r, *res; + mi_wp *ret=NULL; + + r=mi_get_response_blk(h); + res=mi_get_rrecord(r); + + if (res) + ret=mi_parse_wp_res(res); + + mi_free_output(r); + return ret; +} + +char *mi_res_value(mi_h *h) +{ + mi_results *r=mi_res_done_var(h,"value"); + char *s=NULL; + + if (r && r->type==t_const) + { + s=r->v.cstr; + r->v.rs=NULL; + } + mi_free_results(r); + return s; +} + +mi_output *mi_get_stop_record(mi_output *r) +{ + while (r) + { + if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && + r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) + return r; + r=r->next; + } + return r; +} + +static +char *reason_names[]= +{ + "breakpoint-hit", + "watchpoint-trigger", + "read-watchpoint-trigger", + "access-watchpoint-trigger", + "watchpoint-scope", + "function-finished", + "location-reached", + "end-stepping-range", + "exited-signalled", + "exited", + "exited-normally", + "signal-received" +}; + +static +enum mi_stop_reason reason_values[]= +{ + sr_bkpt_hit, + sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, + sr_function_finished, sr_location_reached, sr_end_stepping_range, + sr_exited_signalled, sr_exited, sr_exited_normally, + sr_signal_received +}; + +static +char *reason_expl[]= +{ + "Hit a breakpoint", + "Write watchpoint", + "Read watchpoint", + "Access watchpoint", + "Watchpoint out of scope", + "Function finished", + "Location reached", + "End of stepping", + "Exited signalled", + "Exited with error", + "Exited normally", + "Signal received" +}; + +enum mi_stop_reason mi_reason_str_to_enum(const char *s) +{ + int i; + + for (i=0; itype==t_const) + { + if (strcmp(r->var,"reason")==0) + res->reason=mi_reason_str_to_enum(r->v.cstr); + else if (!res->have_thread_id && strcmp(r->var,"thread-id")==0) + { + res->have_thread_id=1; + res->thread_id=atoi(r->v.cstr); + } + else if (!res->have_bkptno && strcmp(r->var,"bkptno")==0) + { + res->have_bkptno=1; + res->bkptno=atoi(r->v.cstr); + } + else if (!res->have_bkptno && strcmp(r->var,"wpnum")==0) + { + res->have_wpno=1; + res->wpno=atoi(r->v.cstr); + } + else if (strcmp(r->var,"gdb-result-var")==0) + { + res->gdb_result_var=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"return-value")==0) + { + res->return_value=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"signal-name")==0) + { + res->signal_name=r->v.cstr; + r->v.cstr=NULL; + } + else if (strcmp(r->var,"signal-meaning")==0) + { + res->signal_meaning=r->v.cstr; + r->v.cstr=NULL; + } + else if (!res->have_exit_code && strcmp(r->var,"exit-code")==0) + { + res->have_exit_code=1; + res->exit_code=atoi(r->v.cstr); + } + } + else // tuple or list + { + if (strcmp(r->var,"frame")==0) + res->frame=mi_parse_frame(r->v.rs); + else if (!res->wp && strcmp(r->var,"wpt")==0) + res->wp=mi_get_wp(r->v.rs,wm_write); + else if (!res->wp && strcmp(r->var,"hw-rwpt")==0) + res->wp=mi_get_wp(r->v.rs,wm_read); + else if (!res->wp && strcmp(r->var,"hw-awpt")==0) + res->wp=mi_get_wp(r->v.rs,wm_rw); + else if (!(res->wp_old || res->wp_val) && strcmp(r->var,"value")==0) + { + mi_results *p=r->v.rs; + while (p) + { + if (strcmp(p->var,"value")==0 || strcmp(p->var,"new")==0) + { + res->wp_val=p->v.cstr; + p->v.cstr=NULL; + } + else if (strcmp(p->var,"old")==0) + { + res->wp_old=p->v.cstr; + p->v.cstr=NULL; + } + p=p->next; + } + } + } + r=r->next; + } + } + return res; +} + +mi_stop *mi_res_stop(mi_h *h) +{ + mi_output *o=mi_retire_response(h); + mi_stop *stop=NULL; + + if (o) + { + mi_output *sr=mi_get_stop_record(o); + if (sr) + stop=mi_get_stopped(sr->c); + } + mi_free_output(o); + + return stop; +} + +int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, + unsigned long *addr) +{ + char *end; + mi_results *res=mi_res_done_var(h,"memory"), *r; + int ok=0; + + *na=0; + r=res; + if (r && r->type==t_list && ws==1) + { + r=r->v.rs; + if (r->type!=t_tuple) + { + mi_free_results(res); + return 0; + } + r=r->v.rs; + while (r) + { + if (r->type==t_list && strcmp(r->var,"data")==0) + { + mi_results *data=r->v.rs; + ok++; + if (data && data->type==t_const && + strcmp(data->v.cstr,"N/A")==0) + *na=1; + else + while (data) + { + if (data->type==t_const) + *(dest++)=strtol(data->v.cstr,&end,0); + data=data->next; + } + } + else if (r->type==t_const && strcmp(r->var,"addr")==0) + { + ok++; + if (addr) + *addr=strtoul(r->v.cstr,&end,0); + } + r=r->next; + } + + } + mi_free_results(res); + return ok==2; +} + +mi_asm_insn *mi_parse_insn(mi_results *c) +{ + mi_asm_insn *res=NULL, *cur=NULL; + mi_results *sub; + char *end; + + while (c) + { + if (c->type==t_tuple) + { + if (!res) + res=cur=mi_alloc_asm_insn(); + else + { + cur->next=mi_alloc_asm_insn(); + cur=cur->next; + } + if (!cur) + { + mi_free_asm_insn(res); + return NULL; + } + sub=c->v.rs; + while (sub) + { + if (sub->type==t_const) + { + if (strcmp(sub->var,"address")==0) + cur->addr=(void *)strtoul(sub->v.cstr,&end,0); + else if (strcmp(sub->var,"func-name")==0) + { + cur->func=sub->v.cstr; + sub->v.cstr=NULL; + } + else if (strcmp(sub->var,"offset")==0) + cur->offset=atoi(sub->v.cstr); + else if (strcmp(sub->var,"inst")==0) + { + cur->inst=sub->v.cstr; + sub->v.cstr=NULL; + } + } + sub=sub->next; + } + } + c=c->next; + } + return res; +} + +mi_asm_insns *mi_parse_insns(mi_results *c) +{ + mi_asm_insns *res=NULL, *cur=NULL; + mi_results *sub; + + while (c) + { + if (c->var) + { + if (strcmp(c->var,"src_and_asm_line")==0 && c->type==t_tuple) + { + if (!res) + res=cur=mi_alloc_asm_insns(); + else + { + cur->next=mi_alloc_asm_insns(); + cur=cur->next; + } + if (!cur) + { + mi_free_asm_insns(res); + return NULL; + } + sub=c->v.rs; + while (sub) + { + if (sub->var) + { + if (sub->type==t_const) + { + if (strcmp(sub->var,"line")==0) + cur->line=atoi(sub->v.cstr); + else if (strcmp(sub->var,"file")==0) + { + cur->file=sub->v.cstr; + sub->v.cstr=NULL; + } + } + else if (sub->type==t_list) + { + if (strcmp(sub->var,"line_asm_insn")==0) + cur->ins=mi_parse_insn(sub->v.rs); + } + } + sub=sub->next; + } + } + } + else + {/* No source line, just instructions */ + res=mi_alloc_asm_insns(); + res->ins=mi_parse_insn(c); + break; + } + c=c->next; + } + return res; +} + + +mi_asm_insns *mi_get_asm_insns(mi_h *h) +{ + mi_results *r=mi_res_done_var(h,"asm_insns"); + mi_asm_insns *f=NULL; + + if (r && r->type==t_list) + f=mi_parse_insns(r->v.rs); + mi_free_results(r); + return f; +} + +mi_chg_reg *mi_parse_list_regs(mi_results *r, int *how_many) +{ + mi_results *c=r; + int cregs=0; + mi_chg_reg *first=NULL, *cur=NULL; + + /* Create the list. */ + while (c) + { + if (c->type==t_const && !c->var) + { + if (first) + cur=cur->next=mi_alloc_chg_reg(); + else + first=cur=mi_alloc_chg_reg(); + cur->name=c->v.cstr; + cur->reg=cregs++; + c->v.cstr=NULL; + } + c=c->next; + } + if (how_many) + *how_many=cregs; + + return first; +} + +mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many) +{ + mi_results *r=mi_res_done_var(h,"register-names"); + mi_chg_reg *l=NULL; + + if (r && r->type==t_list) + l=mi_parse_list_regs(r->v.rs,how_many); + mi_free_results(r); + return l; +} + +mi_chg_reg *mi_parse_list_changed_regs(mi_results *r) +{ + mi_results *c=r; + mi_chg_reg *first=NULL, *cur=NULL; + + /* Create the list. */ + while (c) + { + if (c->type==t_const && !c->var) + { + if (first) + cur=cur->next=mi_alloc_chg_reg(); + else + first=cur=mi_alloc_chg_reg(); + cur->reg=atoi(c->v.cstr); + } + c=c->next; + } + + return first; +} + +mi_chg_reg *mi_get_list_changed_regs(mi_h *h) +{ + mi_results *r=mi_res_done_var(h,"changed-registers"); + mi_chg_reg *changed=NULL; + + if (r && r->type==t_list) + changed=mi_parse_list_changed_regs(r->v.rs); + mi_free_results(r); + return changed; +} + +int mi_parse_reg_values(mi_results *r, mi_chg_reg *l) +{ + mi_results *c; + + while (r && l) + { + if (r->type==t_tuple && !r->var) + { + c=r->v.rs; + while (c) + { + if (c->type==t_const && c->var) + { + if (strcmp(c->var,"number")==0) + { + if (atoi(c->v.cstr)!=l->reg) + { + mi_error=MI_PARSER; + return 0; + } + } + else if (strcmp(c->var,"value")==0) + { + l->val=c->v.cstr; + c->v.cstr=NULL; + } + } + c=c->next; + } + } + r=r->next; + l=l->next; + } + + return !l && !r; +} + +int mi_get_reg_values(mi_h *h, mi_chg_reg *l) +{ + mi_results *r=mi_res_done_var(h,"register-values"); + int ok=0; + + if (r && r->type==t_list) + ok=mi_parse_reg_values(r->v.rs,l); + mi_free_results(r); + return ok; +} + +int mi_parse_list_regs_l(mi_results *r, mi_chg_reg *l) +{ + while (r && l) + { + if (r->type==t_const && !r->var) + { + free(l->name); + l->name=r->v.cstr; + r->v.cstr=NULL; + l=l->next; + } + r=r->next; + } + + return !l && !r; +} + +int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l) +{ + mi_results *r=mi_res_done_var(h,"register-names"); + int ok=0; + + if (r && r->type==t_list) + ok=mi_parse_list_regs_l(r->v.rs,l); + mi_free_results(r); + return ok; +} + +mi_chg_reg *mi_parse_reg_values_l(mi_results *r, int *how_many) +{ + mi_results *c; + mi_chg_reg *first=NULL, *cur=NULL; + *how_many=0; + + while (r) + { + if (r->type==t_tuple && !r->var) + { + c=r->v.rs; + if (first) + cur=cur->next=mi_alloc_chg_reg(); + else + first=cur=mi_alloc_chg_reg(); + while (c) + { + if (c->type==t_const && c->var) + { + if (strcmp(c->var,"number")==0) + { + cur->reg=atoi(c->v.cstr); + (*how_many)++; + } + else if (strcmp(c->var,"value")==0) + { + cur->val=c->v.cstr; + c->v.cstr=NULL; + } + } + c=c->next; + } + } + r=r->next; + } + + return first; +} + +mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many) +{ + mi_results *r=mi_res_done_var(h,"register-values"); + mi_chg_reg *rgs=NULL; + + if (r && r->type==t_list) + rgs=mi_parse_reg_values_l(r->v.rs,how_many); + mi_free_results(r); + return rgs; +} + + diff --git a/src/prg_control.c b/src/prg_control.c new file mode 100644 index 0000000..f2e3a81 --- /dev/null +++ b/src/prg_control.c @@ -0,0 +1,454 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Program control. + Comments: + GDB/MI commands for the "Program Control" section.@p + +@
    +gdb command:                   Implemented?
    +
    +-exec-abort                    N.A. (*) (kill, but with non-interactive options)
    +-exec-arguments                Yes
    +-exec-continue                 Yes  ASYNC
    +-exec-finish                   Yes  ASYNC
    +-exec-interrupt                Yes  ASYNC
    +-exec-next                     Yes  ASYNC
    +-exec-next-instruction         Yes  ASYNC
    +-exec-return                   Yes
    +-exec-run                      Yes  ASYNC
    +-exec-show-arguments           N.A. (show args) see gmi_stack_info_frame
    +-exec-step                     Yes  ASYNC
    +-exec-step-instruction         Yes  ASYNC
    +-exec-until                    Yes  ASYNC
    +-file-exec-and-symbols         Yes
    +-file-exec-file                No
    +-file-list-exec-sections       N.A. (info file)
    +-file-list-exec-source-files   N.A.
    +-file-list-shared-libraries    N.A.
    +-file-list-symbol-files        N.A.
    +-file-symbol-file              Yes
    +@
    + +(*) gmi_exec_kill implements it, but you should ensure that +gmi_gdb_set("confirm","off") was called.@p + +GDB Bug workaround for -file-exec-and-symbols and -file-symbol-file: This +is complex, but a real bug. When you set a breakpoint you never know the +name of the file as it appears in the debug info. So you can be specifying +an absolute file name or a relative file name. The reference point could be +different than the one used in the debug info. To solve all the combinations +gdb does a search trying various combinations. GDB isn't very smart so you +must at least specify the working directory and the directory where the +binary is located to get a good chance (+ user options to solve the rest). +Once you did it gdb can find the file by doing transformations to the +"canonical" filename. This search works OK for already loaded symtabs +(symbol tables), but it have a bug when the search is done for psymtabs +(partial symtabs). The bug is in the use of source_full_path_of (source.c). +This function calls openp indicating try_cwd_first. It makes the search file +if the psymtab file name have at least one dirseparator. It means that +psymtabs for files compiled with relative paths will fail. The search for +symtabs uses symtab_to_filename, it calls open_source_file which finally +calls openp without try_cwd_first.@* +To workaround this bug we must ensure gdb loads *all* the symtabs to memory. +And here comes another problem -file-exec-and-symbols doesn't support it +according to docs. In real life that's a wrapper for "file", but as nobody +can say it won't change we must use the CLI command. + +***************************************************************************/ + +#include +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_file_exec_and_symbols(mi_h *h, const char *file) +{ + if (mi_get_workaround(MI_PSYM_SEARCH)) + mi_send(h,"file -readnow %s\n",file); + else + mi_send(h,"-file-exec-and-symbols %s\n",file); +} + +void mi_exec_arguments(mi_h *h, const char *args) +{ + mi_send(h,"-exec-arguments %s\n",args); +} + +void mi_exec_run(mi_h *h) +{ + mi_send(h,"-exec-run\n"); +} + +void mi_exec_continue(mi_h *h) +{ + mi_send(h,"-exec-continue\n"); +} + +void mi_target_terminal(mi_h *h, const char *tty_name) +{ + mi_send(h,"tty %s\n",tty_name); +} + +void mi_file_symbol_file(mi_h *h, const char *file) +{ + if (mi_get_workaround(MI_PSYM_SEARCH)) + mi_send(h,"symbol-file -readnow %s\n",file); + else + mi_send(h,"-file-symbol-file %s\n",file); +} + +void mi_exec_finish(mi_h *h) +{ + mi_send(h,"-exec-finish\n"); +} + +void mi_exec_interrupt(mi_h *h) +{ + mi_send(h,"-exec-interrupt\n"); +} + +void mi_exec_next(mi_h *h, int count) +{ + if (count>1) + mi_send(h,"-exec-next %d\n",count); + else + mi_send(h,"-exec-next\n"); +} + +void mi_exec_next_instruction(mi_h *h) +{ + mi_send(h,"-exec-next-instruction\n"); +} + +void mi_exec_step(mi_h *h, int count) +{ + if (count>1) + mi_send(h,"-exec-step %d\n",count); + else + mi_send(h,"-exec-step\n"); +} + +void mi_exec_step_instruction(mi_h *h) +{ + mi_send(h,"-exec-step-instruction\n"); +} + +void mi_exec_until(mi_h *h, const char *file, int line) +{ + if (!file) + mi_send(h,"-exec-until\n"); + else + mi_send(h,"-exec-until %s:%d\n",file,line); +} + +void mi_exec_until_addr(mi_h *h, void *addr) +{ + mi_send(h,"-exec-until *%p\n",addr); +} + +void mi_exec_return(mi_h *h) +{ + mi_send(h,"-exec-return\n"); +} + +void mi_exec_kill(mi_h *h) +{ + mi_send(h,"kill\n"); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Specify the executable and arguments for local debug. + + Command: -file-exec-and-symbols + -exec-arguments + Return: !=0 OK + +***************************************************************************/ + +int gmi_set_exec(mi_h *h, const char *file, const char *args) +{ + mi_file_exec_and_symbols(h,file); + if (!mi_res_simple_done(h)) + return 0; + if (!args) + return 1; + mi_exec_arguments(h,args); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Start running the executable. Remote sessions starts running. + + Command: -exec-run + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_run(mi_h *h) +{ + mi_exec_run(h); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Continue the execution after a "stop". + + Command: -exec-continue + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_continue(mi_h *h) +{ + mi_exec_continue(h); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Indicate which terminal will use the target program. For local sessions. + + Command: tty + Return: !=0 OK + Example: + +***************************************************************************/ + +int gmi_target_terminal(mi_h *h, const char *tty_name) +{ + mi_target_terminal(h,tty_name); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Specify what's the local copy that have debug info. For remote sessions. + + Command: -file-symbol-file + Return: !=0 OK + +***************************************************************************/ + +int gmi_file_symbol_file(mi_h *h, const char *file) +{ + mi_file_symbol_file(h,file); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Continue until function return, the return value is included in the async +response. + + Command: -exec-finish + Return: !=0 OK. + +***************************************************************************/ + +int gmi_exec_finish(mi_h *h) +{ + mi_exec_finish(h); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Stop the program using SIGINT. The corresponding command should be +-exec-interrupt but not even gdb 6.1.1 can do it because the "async" mode +isn't really working. + + Command: -exec-interrupt [replacement] + Return: Always 1 + Example: + +***************************************************************************/ + +int gmi_exec_interrupt(mi_h *h) +{ + // **** IMPORTANT!!! **** Not even gdb 6.1.1 can do it because the "async" + // mode isn't really working. + //mi_exec_interrupt(h); + //return mi_res_simple_running(h); + + kill(h->pid,SIGINT); + return 1; // How can I know? +} + +/**[txh]******************************************************************** + + Description: + Next line of code. + + Command: -exec-next + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_next(mi_h *h) +{ + mi_exec_next(h,1); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Skip count lines of code. + + Command: -exec-next count + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_next_cnt(mi_h *h, int count) +{ + mi_exec_next(h,count); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Next line of assembler code. + + Command: -exec-next-instruction + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_next_instruction(mi_h *h) +{ + mi_exec_next_instruction(h); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Next line of code. Get inside functions. + + Command: -exec-step + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_step(mi_h *h) +{ + mi_exec_step(h,1); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Next count lines of code. Get inside functions. + + Command: -exec-step count + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_step_cnt(mi_h *h, int count) +{ + mi_exec_step(h,count); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Next line of assembler code. Get inside calls. + + Command: -exec-step-instruction + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_step_instruction(mi_h *h) +{ + mi_exec_step_instruction(h); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Execute until location is reached. If file is NULL then is until next +line. + + Command: -exec-until + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_until(mi_h *h, const char *file, int line) +{ + mi_exec_until(h,file,line); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Execute until location is reached. + + Command: -exec-until (using *address) + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_until_addr(mi_h *h, void *addr) +{ + mi_exec_until_addr(h,addr); + return mi_res_simple_running(h); +} + +/**[txh]******************************************************************** + + Description: + Return to previous frame inmediatly. + + Command: -exec-return + Return: A pointer to a new mi_frames structure indicating the current +location. NULL on error. + +***************************************************************************/ + +mi_frames *gmi_exec_return(mi_h *h) +{ + mi_exec_return(h); + return mi_res_frame(h); +} + +/**[txh]******************************************************************** + + Description: + Just kill the program. That's what -exec-abort should do, but it isn't +implemented by gdb. This implementation only works if the interactive mode +is disabled (gmi_gdb_set("confirm","off")). + + Command: -exec-abort [using kill] + Return: !=0 OK + +***************************************************************************/ + +int gmi_exec_kill(mi_h *h) +{ + mi_exec_kill(h); + return mi_res_simple_done(h); +} + diff --git a/src/stack_man.c b/src/stack_man.c new file mode 100644 index 0000000..8e8ed06 --- /dev/null +++ b/src/stack_man.c @@ -0,0 +1,222 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Stack manipulation. + Comments: + GDB/MI commands for the "Stack Manipulation" section.@p + +@
    +gdb command:              Implemented?
    +
    +-stack-info-frame         Yes, implemented as "frame"
    +-stack-info-depth         Yes
    +-stack-list-arguments     Yes
    +-stack-list-frames        Yes
    +-stack-list-locals        Yes
    +-stack-select-frame       Yes
    +@
    + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_stack_list_frames(mi_h *h, int from, int to) +{ + if (from<0) + mi_send(h,"-stack-list-frames\n"); + else + mi_send(h,"-stack-list-frames %d %d\n",from,to); +} + +void mi_stack_list_arguments(mi_h *h, int show, int from, int to) +{ + if (from<0) + mi_send(h,"-stack-list-arguments %d\n",show); + else + mi_send(h,"-stack-list-arguments %d %d %d\n",show,from,to); +} + +void mi_stack_info_frame(mi_h *h) +{ + mi_send(h,"frame\n"); +} + +void mi_stack_info_depth(mi_h *h, int depth) +{ + if (depth<0) + mi_send(h,"-stack-info-depth\n"); + else + mi_send(h,"-stack-info-depth %d\n",depth); +} + +void mi_stack_select_frame(mi_h *h, int framenum) +{ + mi_send(h,"-stack-select-frame %d\n",framenum); +} + +void mi_stack_list_locals(mi_h *h, int show) +{ + mi_send(h,"-stack-list-locals %d\n",show); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + List of frames. Arguments aren't filled. + + Command: -stack-list-frames + Return: A new list of mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_stack_list_frames(mi_h *h) +{ + mi_stack_list_frames(h,-1,-1); + return mi_res_frames_array(h,"stack"); +} + +/**[txh]******************************************************************** + + Description: + List of frames. Arguments aren't filled. Only the frames in the @var{from} + - @var{to} range are returned. + + Command: -stack-list-frames + Return: A new list of mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to) +{ + mi_stack_list_frames(h,from,to); + return mi_res_frames_array(h,"stack"); +} + +/**[txh]******************************************************************** + + Description: + List arguments. Only @var{level} and @var{args} filled. + + Command: -stack-list-arguments + Return: A new list of mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_stack_list_arguments(mi_h *h, int show) +{ + mi_stack_list_arguments(h,show,-1,-1); + return mi_res_frames_array(h,"stack-args"); +} + +/**[txh]******************************************************************** + + Description: + List arguments. Only @var{level} and @var{args} filled. Only for the +frames in the @var{from} - @var{to} range. + + Command: -stack-list-arguments + Return: A new list of mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to) +{ + mi_stack_list_arguments(h,show,from,to); + return mi_res_frames_array(h,"stack-args"); +} + +/**[txh]******************************************************************** + + Description: + Information about the current frame, including args. + + Command: -stack-info-frame [using frame] + Return: A new mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_stack_info_frame(mi_h *h) +{ + mi_stack_info_frame(h); + return mi_res_frame(h); +} + +/**[txh]******************************************************************** + + Description: + Stack info depth. + + Command: -stack-info-depth + Return: The depth or -1 on error. + +***************************************************************************/ + +int gmi_stack_info_depth(mi_h *h, int max_depth) +{ + mi_results *r; + int ret=-1; + + mi_stack_info_depth(h,max_depth); + r=mi_res_done_var(h,"depth"); + if (r && r->type==t_const) + { + ret=atoi(r->v.cstr); + mi_free_results(r); + } + return ret; +} + +/**[txh]******************************************************************** + + Description: + Set stack info depth. + + Command: -stack-info-depth [no args] + Return: The depth or -1 on error. + Example: + +***************************************************************************/ + +int gmi_stack_info_depth_get(mi_h *h) +{ + return gmi_stack_info_depth(h,-1); +} + +/**[txh]******************************************************************** + + Description: + Change current frame. + + Command: -stack-select-frame + Return: !=0 OK + +***************************************************************************/ + +int gmi_stack_select_frame(mi_h *h, int framenum) +{ + mi_stack_select_frame(h,framenum); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + List of local vars. + + Command: -stack-list-locals + Return: A new mi_results tree containing the variables or NULL on error. + +***************************************************************************/ + +mi_results *gmi_stack_list_locals(mi_h *h, int show) +{ + mi_stack_list_locals(h,show); + return mi_res_done_var(h,"locals"); +} + diff --git a/src/symbol_query.c b/src/symbol_query.c new file mode 100644 index 0000000..55e145f --- /dev/null +++ b/src/symbol_query.c @@ -0,0 +1,32 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Symbol query. + Comments: + GDB/MI commands for the "Symbol Query" section.@p + +@
    +gdb command:              Implemented?
    +-symbol-info-address      N.A. (info address, human readable)
    +-symbol-info-file         N.A.
    +-symbol-info-function     N.A.
    +-symbol-info-line         N.A. (info line, human readable)
    +-symbol-info-symbol       N.A. (info symbol, human readable)
    +-symbol-list-functions    N.A. (info functions, human readable)
    +-symbol-list-types        N.A. (info types, human readable)
    +-symbol-list-variables    N.A. (info variables, human readable)
    +-symbol-list-lines        No (gdb 6.x)
    +-symbol-locate            N.A.
    +-symbol-type              N.A. (ptype, human readable)
    +@
    + +Note:@p + +Only one is implemented and not in gdb 5.x.@p + +***************************************************************************/ + +#include "mi_gdb.h" + diff --git a/src/target_man.c b/src/target_man.c new file mode 100644 index 0000000..cf6c815 --- /dev/null +++ b/src/target_man.c @@ -0,0 +1,119 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2007 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Target manipulation. + Comments: + GDB/MI commands for the "Target Manipulation" section.@p + +@
    +-target-attach                  Yes (implemented using attach)
    +-target-compare-sections        N.A. (compare-sections)
    +-target-detach                  Yes
    +-target-download                Yes
    +-target-exec-status             N.A.
    +-target-list-available-targets  N.A. (help target)
    +-target-list-current-targets    N.A. (info file among other things)
    +-target-list-parameters         N.A.
    +-target-select                  Yes
    +@
    + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_target_select(mi_h *h, const char *type, const char *params) +{ + mi_send(h,"-target-select %s %s\n",type,params); +} + +/* Note: -target-attach isn't currently implemented :-( (gdb 6.1.1) */ +void mi_target_attach(mi_h *h, pid_t pid) +{ + mi_send(h,"attach %d\n",pid); +} + +void mi_target_detach(mi_h *h) +{ + mi_send(h,"-target-detach\n"); +} + +void mi_target_download(mi_h *h) +{ + mi_send(h,"-target-download\n"); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Connect to a remote gdbserver using the specified methode. + + Command: -target-select + Return: !=0 OK + +***************************************************************************/ + +int gmi_target_select(mi_h *h, const char *type, const char *params) +{ + mi_target_select(h,type,params); + if (!mi_res_simple_connected(h)) + return 0; + mi_send_target_commands(h); + return 1; +} + +/**[txh]******************************************************************** + + Description: + Attach to an already running process. + + Command: -target-attach [using attach] + Return: The frame of the current location, NULL on error. + +***************************************************************************/ + +mi_frames *gmi_target_attach(mi_h *h, pid_t pid) +{ + mi_target_attach(h,pid); + //return mi_res_simple_done(h); + return mi_res_frame(h); +} + +/**[txh]******************************************************************** + + Description: + Detach from an attached process. + + Command: -target-detach + Return: !=0 OK + +***************************************************************************/ + +int gmi_target_detach(mi_h *h) +{ + mi_target_detach(h); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Loads the executable onto the remote target. + + Command: -target-download + Return: !=0 OK + +***************************************************************************/ + +int gmi_target_download(mi_h *h) +{ + mi_target_download(h); + // TODO: this response have some data + return mi_res_simple_done(h); +} + diff --git a/src/thread.c b/src/thread.c new file mode 100644 index 0000000..51a333f --- /dev/null +++ b/src/thread.c @@ -0,0 +1,89 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Thread commands. + Comments: + GDB/MI commands for the "Thread Commands" section.@p + +@
    +gdb command:              Implemented?
    +-thread-info              N.A.
    +-thread-list-all-threads  Yes, implemented as "info threads"
    +-thread-list-ids          Yes
    +-thread-select            Yes
    +@
    + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_thread_list_ids(mi_h *h) +{ + mi_send(h,"-thread-list-ids\n"); +} + +void mi_thread_select(mi_h *h, int id) +{ + mi_send(h,"-thread-select %d\n",id); +} + +void mi_thread_list_all_threads(mi_h *h) +{ + mi_send(h,"info threads\n"); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + List available thread ids. + + Command: -thread-list-ids + Return: !=0 OK + +***************************************************************************/ + +int gmi_thread_list_ids(mi_h *h, int **list) +{ + mi_thread_list_ids(h); + return mi_res_thread_ids(h,list); +} + +/**[txh]******************************************************************** + + Description: + Select a thread. + + Command: -thread-select + Return: A new mi_frames or NULL on error. + +***************************************************************************/ + +mi_frames *gmi_thread_select(mi_h *h, int id) +{ + mi_thread_select(h,id); + return mi_res_frame(h); +} + +/**[txh]******************************************************************** + + Description: + Get a list of frames for each available thread. Implemented using "info +thread". + + Command: -thread-list-all-threads + Return: A kist of frames, NULL on error + +***************************************************************************/ + +mi_frames *gmi_thread_list_all_threads(mi_h *h) +{ + mi_thread_list_all_threads(h); + return mi_res_frames_list(h); +} + diff --git a/src/var_obj.c b/src/var_obj.c new file mode 100644 index 0000000..a027d67 --- /dev/null +++ b/src/var_obj.c @@ -0,0 +1,369 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Module: Variable objects. + Comments: + GDB/MI commands for the "Variable Objects" section.@p + +@
    +gdb command:              Imp? Description:
    +-var-create               Yes  create a variable object
    +-var-delete               Yes  delete the variable object and its children
    +-var-set-format           Yes  set the display format of this variable
    +-var-show-format          Yes  show the display format of this variable
    +-var-info-num-children    Yes  tells how many children this object has
    +-var-list-children        Yes* return a list of the object's children
    +-var-info-type            Yes  show the type of this variable object
    +-var-info-expression      Yes  print what this variable object represents
    +-var-show-attributes      Yes  is this variable editable?
    +-var-evaluate-expression  Yes  get the value of this variable
    +-var-assign               Yes  set the value of this variable
    +-var-update               Yes* update the variable and its children
    +@
    + +Notes:@p +1) I suggest letting gdb to choose the names for the variables.@* +2) -var-list-children supports an optional "show values" argument in MI v2. +It isn't implemented.@* +@p + +* MI v1 and v2 result formats supported.@p + +***************************************************************************/ + +#include "mi_gdb.h" + +/* Low level versions. */ + +void mi_var_create(mi_h *h, const char *name, int frame, const char *exp) +{ + const char *n=name ? name : "-"; + + if (frame<0) + mi_send(h,"-var-create %s * %s\n",n,exp); + else + mi_send(h,"-var-create %s %d %s\n",n,frame,exp); +} + +void mi_var_delete(mi_h *h, const char *name) +{ + mi_send(h,"-var-delete %s\n",name); +} + +void mi_var_set_format(mi_h *h, const char *name, const char *format) +{ + mi_send(h,"-var-set-format \"%s\" %s\n",name,format); +} + +void mi_var_show_format(mi_h *h, const char *name) +{ + mi_send(h,"-var-show-format \"%s\"\n",name); +} + +void mi_var_info_num_children(mi_h *h, const char *name) +{ + mi_send(h,"-var-info-num-children \"%s\"\n",name); +} + +void mi_var_info_type(mi_h *h, const char *name) +{ + mi_send(h,"-var-info-type \"%s\"\n",name); +} + +void mi_var_info_expression(mi_h *h, const char *name) +{ + mi_send(h,"-var-info-expression \"%s\"\n",name); +} + +void mi_var_show_attributes(mi_h *h, const char *name) +{ + mi_send(h,"-var-show-attributes \"%s\"\n",name); +} + +void mi_var_update(mi_h *h, const char *name) +{ + if (name) + mi_send(h,"-var-update %s\n",name); + else + mi_send(h,"-var-update *\n"); +} + +void mi_var_assign(mi_h *h, const char *name, const char *expression) +{ + mi_send(h,"-var-assign \"%s\" \"%s\"\n",name,expression); +} + +void mi_var_evaluate_expression(mi_h *h, const char *name) +{ + mi_send(h,"-var-evaluate-expression \"%s\"\n",name); +} + +void mi_var_list_children(mi_h *h, const char *name) +{ + if (h->version>=MI_VERSION2U(2,0,0)) + mi_send(h,"-var-list-children --all-values \"%s\"\n",name); + else + mi_send(h,"-var-list-children \"%s\"\n",name); +} + +/* High level versions. */ + +/**[txh]******************************************************************** + + Description: + Create a variable object. I recommend using @x{gmi_var_create} and letting +gdb choose the names. + + Command: -var-create + Return: A new mi_gvar strcture or NULL on error. + +***************************************************************************/ + +mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp) +{ + mi_var_create(h,name,frame,exp); + return mi_res_gvar(h,NULL,exp); +} + +/**[txh]******************************************************************** + + Description: + Create a variable object. The name is selected by gdb. Alternative: +@x{gmi_full_var_create}. + + Command: -var-create [auto name] + Return: A new mi_gvar strcture or NULL on error. + +***************************************************************************/ + +mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp) +{ + return gmi_var_create_nm(h,NULL,frame,exp); +} + +/**[txh]******************************************************************** + + Description: + Delete a variable object. Doesn't free the mi_gvar data. + + Command: -var-delete + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_delete(mi_h *h, mi_gvar *var) +{ + mi_var_delete(h,var->name); + return mi_res_simple_done(h); +} + +/**[txh]******************************************************************** + + Description: + Set the format used to represent the result. + + Command: -var-set-format + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format) +{ + int ret; + + mi_var_set_format(h,var->name,mi_format_enum_to_str(format)); + ret=mi_res_simple_done(h); + if (ret) + var->format=format; + return ret; +} + +/**[txh]******************************************************************** + + Description: + Fill the format field with info from gdb. + + Command: -var-show-format + Return: !=0 OK. + +***************************************************************************/ + +int gmi_var_show_format(mi_h *h, mi_gvar *var) +{ + mi_var_show_format(h,var->name); + return mi_res_gvar(h,var,NULL)!=NULL; +} + +/**[txh]******************************************************************** + + Description: + Fill the numchild field with info from gdb. + + Command: -var-info-num-children + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_info_num_children(mi_h *h, mi_gvar *var) +{ + mi_var_info_num_children(h,var->name); + return mi_res_gvar(h,var,NULL)!=NULL; +} + +/**[txh]******************************************************************** + + Description: + Fill the type field with info from gdb. + + Command: -var-info-type + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_info_type(mi_h *h, mi_gvar *var) +{ + mi_var_info_type(h,var->name); + return mi_res_gvar(h,var,NULL)!=NULL; +} + +/**[txh]******************************************************************** + + Description: + Fill the expression and lang fields with info from gdb. Note that lang +isn't filled during creation. + + Command: -var-info-expression + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_info_expression(mi_h *h, mi_gvar *var) +{ + mi_var_info_expression(h,var->name); + return mi_res_gvar(h,var,NULL)!=NULL; +} + + +/**[txh]******************************************************************** + + Description: + Fill the attr field with info from gdb. Note that attr isn't filled +during creation. + + Command: -var-show-attributes + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_show_attributes(mi_h *h, mi_gvar *var) +{ + mi_var_show_attributes(h,var->name); + return mi_res_gvar(h,var,NULL)!=NULL; +} + +/**[txh]******************************************************************** + + Description: + Create the variable and also fill the lang and attr fields. The name is +selected by gdb. + + Command: -var-create + -var-info-expression + -var-show-attributes + Return: A new mi_gvar strcture or NULL on error. + +***************************************************************************/ + +mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp) +{ + mi_gvar *var=gmi_var_create_nm(h,NULL,frame,exp); + if (var) + {/* What if it fails? */ + gmi_var_info_expression(h,var); + gmi_var_show_attributes(h,var); + } + return var; +} + +/**[txh]******************************************************************** + + Description: + Update variable. Use NULL for all. Note that *changed can be NULL if none +updated. + + Command: -var-update + Return: !=0 OK. The @var{changed} list contains the list of changed vars. + +***************************************************************************/ + +int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed) +{ + mi_var_update(h,var ? var->name : NULL); + return mi_res_changelist(h,changed); +} + +/**[txh]******************************************************************** + + Description: + Change variable. The new value replaces the @var{value} field. + + Command: -var-assign + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression) +{ + char *res; + mi_var_assign(h,var->name,expression); + res=mi_res_value(h); + if (res) + { + free(var->value); + var->value=res; + return 1; + } + return 0; +} + +/**[txh]******************************************************************** + + Description: + Fill the value field getting the current value for a variable. + + Command: -var-evaluate-expression + Return: !=0 OK, value contains the result. + +***************************************************************************/ + +int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var) +{ + char *s; + + mi_var_evaluate_expression(h,var->name); + s=mi_res_value(h); + if (s) + { + free(var->value); + var->value=s; + } + return s!=NULL; +} + +/**[txh]******************************************************************** + + Description: + List children. It ONLY returns the first level information. :-(@* + On success the child field contains the list of children. + + Command: -var-list-children + Return: !=0 OK + +***************************************************************************/ + +int gmi_var_list_children(mi_h *h, mi_gvar *var) +{ + mi_var_list_children(h,var->name); + return mi_res_children(h,var); +} + diff --git a/version.c b/version.c new file mode 100644 index 0000000..b9b78c4 --- /dev/null +++ b/version.c @@ -0,0 +1,7 @@ +#include "src/mi_gdb.h" + +int main() +{ + fputs(MI_VERSION_STR,stdout); + return 0; +} -- 1.8.3.1