From: Christian Grothoff Date: Fri, 2 Jul 2010 14:01:16 +0000 (+0000) Subject: rename fest X-Git-Tag: initial-import-from-subversion-38251~21056 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=bde3e5116229db9c1b95d1db8de1b1a00f284916;p=oweals%2Fgnunet.git rename fest --- diff --git a/src/monkey/Makefile.am b/src/monkey/Makefile.am index 0229c67c9..8fee75733 100644 --- a/src/monkey/Makefile.am +++ b/src/monkey/Makefile.am @@ -12,55 +12,41 @@ endif if !MINGW -lib_LTLIBRARIES = libgnunetmonkey.la - -libgnunetmonkey_la_SOURCES = \ - monkey_api.c monkey.h -libgnunetmonkey_la_LIBADD = \ - $(top_builddir)/src/util/libgnunetutil.la \ - -lesmtp -libgnunetmonkey_la_LDFLAGS = \ - $(GN_LIB_LDFLAGS) $(WINFLAGS) \ - -version-info 0:0:0 - - bin_PROGRAMS = \ gnunet-monkey \ - gnunet-service-monkey \ - bug_null_pointer_exception \ - mail_sender + gnunet-service-monkey + +noinst_PROGRAMS = \ + bug_null_pointer_exception gnunet_monkey_SOURCES = \ - mi_gdb.h \ - alloc.c \ - breakpoint.c \ - connect.c \ - cpp_int.cc \ - data_man.c \ - error.c \ - get_free_pty.c \ - get_free_vt.c \ - gnunet-monkey.c \ - misc.c \ - parse.c \ - prg_control.c \ - stack_man.c \ - symbol_query.c \ - target_man.c \ - thread.c \ - var_obj.c - + gdbmi.h \ + gdbmi_alloc.c \ + gdbmi_breakpoint.c \ + gdbmi_connect.c \ + gdbmi_cpp_int.cc \ + gdbmi_data_man.c \ + gdbmi_error.c \ + gdbmi_get_free_pty.c \ + gdbmi_get_free_vt.c \ + gdbmi_misc.c \ + gdbmi_parse.c \ + gdbmi_prg_control.c \ + gdbmi_stack_man.c \ + gdbmi_symbol_query.c \ + gdbmi_target_man.c \ + gdbmi_thread.c \ + gdbmi_var_obj.c \ + gnunet-monkey.c + gnunet_monkey_LDADD = \ - $(top_builddir)/src/monkey/libgnunetmonkey.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) - gnunet_service_monkey_SOURCES = \ gnunet-service-monkey.c gnunet_service_monkey_LDADD = \ - $(top_builddir)/src/monkey/libgnunetmonkey.la \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) @@ -68,35 +54,23 @@ gnunet_service_monkey_LDADD = \ mail_sender_SOURCES = \ mail_sender.c mail_sender_LDADD = \ - $(top_builddir)/src/monkey/libgnunetmonkey.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(GN_LIBINTL) + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) bug_null_pointer_exception_SOURCES = \ bug_null_pointer_exception.c bug_null_pointer_exception_LDADD = \ - $(top_builddir)/src/monkey/libgnunetmonkey.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(GN_LIBINTL) + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) - -check_PROGRAMS = \ - test_monkey_api - #TESTS = $(check_PROGRAMS) $(check_SCRIPTS) -test_monkey_api_SOURCES = \ - test_monkey_api.c -test_monkey_api_LDADD = \ - $(top_builddir)/src/monkey/libgnunetmonkey.la \ - $(top_builddir)/src/util/libgnunetutil.la - check_SCRIPTS = \ test_gnunet_monkey.sh EXTRA_DIST = \ - test_monkey_api_data.conf \ + test_gnunet_monkey_data.conf \ $(check_SCRIPTS) endif diff --git a/src/monkey/alloc.c b/src/monkey/alloc.c deleted file mode 100644 index 9e1ffa740..000000000 --- a/src/monkey/alloc.c +++ /dev/null @@ -1,308 +0,0 @@ -/**[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/monkey/breakpoint.c b/src/monkey/breakpoint.c deleted file mode 100644 index 645c27444..000000000 --- a/src/monkey/breakpoint.c +++ /dev/null @@ -1,265 +0,0 @@ -/**[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/monkey/connect.c b/src/monkey/connect.c deleted file mode 100644 index eaca967da..000000000 --- a/src/monkey/connect.c +++ /dev/null @@ -1,884 +0,0 @@ -/**[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; - } - 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/monkey/cpp_int.cc b/src/monkey/cpp_int.cc deleted file mode 100644 index 815d6f7b7..000000000 --- a/src/monkey/cpp_int.cc +++ /dev/null @@ -1,1123 +0,0 @@ -/**[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/monkey/data_man.c b/src/monkey/data_man.c deleted file mode 100644 index a7e9f11bf..000000000 --- a/src/monkey/data_man.c +++ /dev/null @@ -1,241 +0,0 @@ -/**[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/monkey/error.c b/src/monkey/error.c deleted file mode 100644 index adeb16c16..000000000 --- a/src/monkey/error.c +++ /dev/null @@ -1,38 +0,0 @@ -/**[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/monkey/gdbmi.h b/src/monkey/gdbmi.h new file mode 100644 index 000000000..df932b4ec --- /dev/null +++ b/src/monkey/gdbmi.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/monkey/gdbmi_alloc.c b/src/monkey/gdbmi_alloc.c new file mode 100644 index 000000000..9e1ffa740 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_breakpoint.c b/src/monkey/gdbmi_breakpoint.c new file mode 100644 index 000000000..645c27444 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_connect.c b/src/monkey/gdbmi_connect.c new file mode 100644 index 000000000..eaca967da --- /dev/null +++ b/src/monkey/gdbmi_connect.c @@ -0,0 +1,884 @@ +/**[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; + } + 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/monkey/gdbmi_cpp_int.cc b/src/monkey/gdbmi_cpp_int.cc new file mode 100644 index 000000000..815d6f7b7 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_data_man.c b/src/monkey/gdbmi_data_man.c new file mode 100644 index 000000000..a7e9f11bf --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_error.c b/src/monkey/gdbmi_error.c new file mode 100644 index 000000000..adeb16c16 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_get_free_pty.c b/src/monkey/gdbmi_get_free_pty.c new file mode 100644 index 000000000..8c145d0db --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_get_free_vt.c b/src/monkey/gdbmi_get_free_vt.c new file mode 100644 index 000000000..f5c980005 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_misc.c b/src/monkey/gdbmi_misc.c new file mode 100644 index 000000000..5440ab0f0 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_parse.c b/src/monkey/gdbmi_parse.c new file mode 100644 index 000000000..0dea6cb5d --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_prg_control.c b/src/monkey/gdbmi_prg_control.c new file mode 100644 index 000000000..671725f94 --- /dev/null +++ b/src/monkey/gdbmi_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 %s -readnow\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 %s -readnow\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/monkey/gdbmi_stack_man.c b/src/monkey/gdbmi_stack_man.c new file mode 100644 index 000000000..8e8ed0691 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_symbol_query.c b/src/monkey/gdbmi_symbol_query.c new file mode 100644 index 000000000..55e145f87 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_target_man.c b/src/monkey/gdbmi_target_man.c new file mode 100644 index 000000000..cf6c815b2 --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_thread.c b/src/monkey/gdbmi_thread.c new file mode 100644 index 000000000..51a333f5b --- /dev/null +++ b/src/monkey/gdbmi_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/monkey/gdbmi_var_obj.c b/src/monkey/gdbmi_var_obj.c new file mode 100644 index 000000000..a027d6751 --- /dev/null +++ b/src/monkey/gdbmi_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/src/monkey/get_free_pty.c b/src/monkey/get_free_pty.c deleted file mode 100644 index 8c145d0db..000000000 --- a/src/monkey/get_free_pty.c +++ /dev/null @@ -1,132 +0,0 @@ -/**[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/monkey/get_free_vt.c b/src/monkey/get_free_vt.c deleted file mode 100644 index f5c980005..000000000 --- a/src/monkey/get_free_vt.c +++ /dev/null @@ -1,153 +0,0 @@ -/**[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/monkey/libmigdb.h b/src/monkey/libmigdb.h deleted file mode 100644 index c4c404def..000000000 --- a/src/monkey/libmigdb.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef LIBMIGDB_H_ -#define LIBMIGDB_H_ - -#endif /*LIBMIGDB_H_*/ diff --git a/src/monkey/mi_gdb.h b/src/monkey/mi_gdb.h deleted file mode 100644 index df932b4ec..000000000 --- a/src/monkey/mi_gdb.h +++ /dev/null @@ -1,972 +0,0 @@ -/**[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/monkey/misc.c b/src/monkey/misc.c deleted file mode 100644 index 5440ab0f0..000000000 --- a/src/monkey/misc.c +++ /dev/null @@ -1,118 +0,0 @@ -/**[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/monkey/monkey.h b/src/monkey/monkey.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/monkey/monkey_api.c b/src/monkey/monkey_api.c deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/monkey/parse.c b/src/monkey/parse.c deleted file mode 100644 index 0dea6cb5d..000000000 --- a/src/monkey/parse.c +++ /dev/null @@ -1,1923 +0,0 @@ -/**[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/monkey/prg_control.c b/src/monkey/prg_control.c deleted file mode 100644 index 671725f94..000000000 --- a/src/monkey/prg_control.c +++ /dev/null @@ -1,454 +0,0 @@ -/**[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 %s -readnow\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 %s -readnow\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/monkey/stack_man.c b/src/monkey/stack_man.c deleted file mode 100644 index 8e8ed0691..000000000 --- a/src/monkey/stack_man.c +++ /dev/null @@ -1,222 +0,0 @@ -/**[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/monkey/symbol_query.c b/src/monkey/symbol_query.c deleted file mode 100644 index 55e145f87..000000000 --- a/src/monkey/symbol_query.c +++ /dev/null @@ -1,32 +0,0 @@ -/**[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/monkey/target_man.c b/src/monkey/target_man.c deleted file mode 100644 index cf6c815b2..000000000 --- a/src/monkey/target_man.c +++ /dev/null @@ -1,119 +0,0 @@ -/**[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/monkey/test_gnunet_monkey_data.conf b/src/monkey/test_gnunet_monkey_data.conf new file mode 100644 index 000000000..fc9153b1a --- /dev/null +++ b/src/monkey/test_gnunet_monkey_data.conf @@ -0,0 +1,38 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-arm/ +DEFAULTCONFIG = test_arm_api_data.conf + +[arm] +PORT = 23354 +DEFAULTSERVICES = +BINARY = gnunet-service-arm +OPTIONS = -L ERROR +# DEBUG = YES +#PREFIX = valgrind --tool=memcheck --leak-check=yes + +[gnunet-monkey] +# DEBUG = YES +PORT = 23355 +BINARY = gnunet-monkey +# PREFIX = valgrind + + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[core] +AUTOSTART = NO + +[transport] +AUTOSTART = NO + +[peerinfo] +AUTOSTART = NO + +[statistics] +AUTOSTART = YES +# DEBUG = NO + diff --git a/src/monkey/test_monkey_api.c b/src/monkey/test_monkey_api.c deleted file mode 100644 index d63453bee..000000000 --- a/src/monkey/test_monkey_api.c +++ /dev/null @@ -1,6 +0,0 @@ - - -int main(int argc, char *argv[]) -{ - return 0; -} \ No newline at end of file diff --git a/src/monkey/test_monkey_api_data.conf b/src/monkey/test_monkey_api_data.conf deleted file mode 100644 index fc9153b1a..000000000 --- a/src/monkey/test_monkey_api_data.conf +++ /dev/null @@ -1,38 +0,0 @@ -[PATHS] -SERVICEHOME = /tmp/test-gnunetd-arm/ -DEFAULTCONFIG = test_arm_api_data.conf - -[arm] -PORT = 23354 -DEFAULTSERVICES = -BINARY = gnunet-service-arm -OPTIONS = -L ERROR -# DEBUG = YES -#PREFIX = valgrind --tool=memcheck --leak-check=yes - -[gnunet-monkey] -# DEBUG = YES -PORT = 23355 -BINARY = gnunet-monkey -# PREFIX = valgrind - - -[fs] -AUTOSTART = NO - -[datastore] -AUTOSTART = NO - -[core] -AUTOSTART = NO - -[transport] -AUTOSTART = NO - -[peerinfo] -AUTOSTART = NO - -[statistics] -AUTOSTART = YES -# DEBUG = NO - diff --git a/src/monkey/thread.c b/src/monkey/thread.c deleted file mode 100644 index 51a333f5b..000000000 --- a/src/monkey/thread.c +++ /dev/null @@ -1,89 +0,0 @@ -/**[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/monkey/var_obj.c b/src/monkey/var_obj.c deleted file mode 100644 index a027d6751..000000000 --- a/src/monkey/var_obj.c +++ /dev/null @@ -1,369 +0,0 @@ -/**[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); -} -