From 1e2ec555753751d032dbe1f19d5206a2da4de65a Mon Sep 17 00:00:00 2001 From: "Safey A.Halim" Date: Tue, 29 Jun 2010 19:14:37 +0000 Subject: Adding "Monkey", GNUnet module for automatic debugging. Experimental code so far. --- configure.ac | 1 + src/Makefile.am | 5 + src/monkey/Makefile.am | 100 ++ src/monkey/alloc.c | 308 +++++ src/monkey/breakpoint.c | 265 +++++ src/monkey/bug_null_pointer_exception.c | 27 + src/monkey/connect.c | 884 ++++++++++++++ src/monkey/cpp_int.cc | 1123 ++++++++++++++++++ src/monkey/data_man.c | 241 ++++ src/monkey/error.c | 38 + src/monkey/get_free_pty.c | 132 +++ src/monkey/get_free_vt.c | 153 +++ src/monkey/gnunet-monkey.c | 159 +++ src/monkey/gnunet-service-monkey.c | 6 + src/monkey/libmigdb.h | 4 + src/monkey/mail_sender.c | 235 ++++ src/monkey/mi_gdb.h | 972 ++++++++++++++++ src/monkey/misc.c | 118 ++ src/monkey/monkey.h | 0 src/monkey/monkey_api.c | 0 src/monkey/parse.c | 1923 +++++++++++++++++++++++++++++++ src/monkey/prg_control.c | 454 ++++++++ src/monkey/stack_man.c | 222 ++++ src/monkey/symbol_query.c | 32 + src/monkey/target_man.c | 119 ++ src/monkey/test_gnunet_monkey.sh | 199 ++++ src/monkey/test_monkey_api.c | 6 + src/monkey/thread.c | 89 ++ src/monkey/var_obj.c | 369 ++++++ 29 files changed, 8184 insertions(+) create mode 100644 src/monkey/Makefile.am create mode 100644 src/monkey/alloc.c create mode 100644 src/monkey/breakpoint.c create mode 100644 src/monkey/bug_null_pointer_exception.c create mode 100644 src/monkey/connect.c create mode 100644 src/monkey/cpp_int.cc create mode 100644 src/monkey/data_man.c create mode 100644 src/monkey/error.c create mode 100644 src/monkey/get_free_pty.c create mode 100644 src/monkey/get_free_vt.c create mode 100644 src/monkey/gnunet-monkey.c create mode 100644 src/monkey/gnunet-service-monkey.c create mode 100644 src/monkey/libmigdb.h create mode 100644 src/monkey/mail_sender.c create mode 100644 src/monkey/mi_gdb.h create mode 100644 src/monkey/misc.c create mode 100644 src/monkey/monkey.h create mode 100644 src/monkey/monkey_api.c create mode 100644 src/monkey/parse.c create mode 100644 src/monkey/prg_control.c create mode 100644 src/monkey/stack_man.c create mode 100644 src/monkey/symbol_query.c create mode 100644 src/monkey/target_man.c create mode 100755 src/monkey/test_gnunet_monkey.sh create mode 100644 src/monkey/test_monkey_api.c create mode 100644 src/monkey/thread.c create mode 100644 src/monkey/var_obj.c diff --git a/configure.ac b/configure.ac index f484f9295..0abacd253 100644 --- a/configure.ac +++ b/configure.ac @@ -673,6 +673,7 @@ src/testing/Makefile src/topology/Makefile src/transport/Makefile src/util/Makefile +src/monkey/Makefile ]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index cca2c0294..cdab96621 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,12 +6,17 @@ if !MINGW NAT_DIR = nat endif +if HAVE_ESMTP + MONKEY_DIR = monkey +endif + SUBDIRS = \ include $(INTLEMU_SUBDIRS) \ util \ block \ statistics \ arm \ + $(MONKEY_DIR) \ hello \ peerinfo \ datacache \ diff --git a/src/monkey/Makefile.am b/src/monkey/Makefile.am new file mode 100644 index 000000000..516f8c08e --- /dev/null +++ b/src/monkey/Makefile.am @@ -0,0 +1,100 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + + +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_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 + +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) + + +mail_sender_SOURCES = \ + mail_sender.c +mail_sender_LDADD = \ + $(top_builddir)/src/monkey/libgnunetmonkey.la \ + $(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) + + + +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 \ + $(check_SCRIPTS) + + diff --git a/src/monkey/alloc.c b/src/monkey/alloc.c new file mode 100644 index 000000000..9e1ffa740 --- /dev/null +++ b/src/monkey/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/breakpoint.c b/src/monkey/breakpoint.c new file mode 100644 index 000000000..645c27444 --- /dev/null +++ b/src/monkey/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/bug_null_pointer_exception.c b/src/monkey/bug_null_pointer_exception.c new file mode 100644 index 000000000..27428816d --- /dev/null +++ b/src/monkey/bug_null_pointer_exception.c @@ -0,0 +1,27 @@ +#include +#include + +void crashFunction() +{ + //char *stringCannotBeChanged = "String cannot be changed!"; + char *nullString = NULL; + + printf("Now the program will crash! Take a cover! \n"); + //*stringCannotBeChanged = 'h'; + printf("Nonsense!\n"); + if (strcmp(nullString, "A string to compare with") == 0) { + printf("How come?! It had to be crashed!\n"); + } +} + +int main(int argc, char *argv[]) +{ + int i = 0; + printf("arguments: %d\n", argc); + for (i=0; i +#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 new file mode 100644 index 000000000..815d6f7b7 --- /dev/null +++ b/src/monkey/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/data_man.c b/src/monkey/data_man.c new file mode 100644 index 000000000..a7e9f11bf --- /dev/null +++ b/src/monkey/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/error.c b/src/monkey/error.c new file mode 100644 index 000000000..adeb16c16 --- /dev/null +++ b/src/monkey/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/get_free_pty.c b/src/monkey/get_free_pty.c new file mode 100644 index 000000000..8c145d0db --- /dev/null +++ b/src/monkey/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/get_free_vt.c b/src/monkey/get_free_vt.c new file mode 100644 index 000000000..f5c980005 --- /dev/null +++ b/src/monkey/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/gnunet-monkey.c b/src/monkey/gnunet-monkey.c new file mode 100644 index 000000000..f8b0e9e93 --- /dev/null +++ b/src/monkey/gnunet-monkey.c @@ -0,0 +1,159 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004 by Salvador E. Tropea. + Covered by the GPL license. + + Comment: + X11 example/test of the libmigdb. + Run it from an X11 terminal (xterm, Eterm, etc.). + +***************************************************************************/ + +#include +#include //usleep +#include +#include "mi_gdb.h" + +void cb_console(const char *str, void *data) +{ + printf("CONSOLE> %s\n",str); +} + +/* Note that unlike what's documented in gdb docs it isn't usable. */ +void cb_target(const char *str, void *data) +{ + printf("TARGET> %s\n",str); +} + +void cb_log(const char *str, void *data) +{ + printf("LOG> %s\n",str); +} + +void cb_to(const char *str, void *data) +{ + printf(">> %s",str); +} + +void cb_from(const char *str, void *data) +{ + printf("<< %s\n",str); +} + +volatile int async_c=0; + +void cb_async(mi_output *o, void *data) +{ + printf("ASYNC\n"); + async_c++; +} + +int wait_for_stop(mi_h *h) +{ + int res=1; + mi_stop *sr; + mi_frames *f; + + while (!mi_get_response(h)) + usleep(1000); + /* The end of the async. */ + sr=mi_res_stop(h); + if (sr) + { + printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); + printf("Received signal name: %s\n", sr->signal_name); + printf("Received signal meaning: %s\n", sr->signal_meaning); + //printf("In file: %s\n", sr->frame->file); + //printf("Line Number: %d\n", sr->frame->line); + f = gmi_stack_info_frame(h); + mi_free_stop(sr); + } + else + { + printf("Error while waiting\n"); + printf("mi_error: %d\nmi_error_from_gdb: %s\n",mi_error,mi_error_from_gdb); + res=0; + } + return res; +} + +int main(int argc, char *argv[]) +{ + mi_aux_term *xterm_tty=NULL; + + /* This is like a file-handle for fopen. + Here we have all the state of gdb "connection". */ + mi_h *h; + + /* Connect to gdb child. */ + h=mi_connect_local(); + if (!h) + { + printf("Connect failed\n"); + return 1; + } + printf("Connected to gdb!\n"); + + /* Set all callbacks. */ + mi_set_console_cb(h,cb_console,NULL); + mi_set_target_cb(h,cb_target,NULL); + mi_set_log_cb(h,cb_log,NULL); + mi_set_async_cb(h,cb_async,NULL); + mi_set_to_gdb_cb(h,cb_to,NULL); + mi_set_from_gdb_cb(h,cb_from,NULL); + + /* Set the name of the child and the command line aguments. */ + if (!gmi_set_exec(h,"bug_null_pointer_exception", NULL)) + { + printf("Error setting exec y args\n"); + mi_disconnect(h); + return 1; + } + + /* Tell gdb to attach the child to a terminal. */ + if (!gmi_target_terminal(h, ttyname(STDIN_FILENO))) + { + printf("Error selecting target terminal\n"); + mi_disconnect(h); + return 1; + } + + /* Run the program. */ + if (!gmi_exec_run(h)) + { + printf("Error in run!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be stopped when the program crashes */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Continue execution. */ + if (!gmi_exec_continue(h)) + { + printf("Error in continue!\n"); + mi_disconnect(h); + return 1; + } + /* Here we should be terminated. */ + if (!wait_for_stop(h)) + { + mi_disconnect(h); + return 1; + } + + /* Exit from gdb. */ + gmi_gdb_exit(h); + /* Close the connection. */ + mi_disconnect(h); + /* Wait 5 seconds and close the auxiliar terminal. */ + printf("Waiting 5 seconds\n"); + sleep(5); + gmi_end_aux_term(xterm_tty); + + return 0; +} diff --git a/src/monkey/gnunet-service-monkey.c b/src/monkey/gnunet-service-monkey.c new file mode 100644 index 000000000..b0a0583ee --- /dev/null +++ b/src/monkey/gnunet-service-monkey.c @@ -0,0 +1,6 @@ + + +int main(int argc, char *argv[]) +{ + return 0; +} \ No newline at end of file diff --git a/src/monkey/libmigdb.h b/src/monkey/libmigdb.h new file mode 100644 index 000000000..c4c404def --- /dev/null +++ b/src/monkey/libmigdb.h @@ -0,0 +1,4 @@ +#ifndef LIBMIGDB_H_ +#define LIBMIGDB_H_ + +#endif /*LIBMIGDB_H_*/ diff --git a/src/monkey/mail_sender.c b/src/monkey/mail_sender.c new file mode 100644 index 000000000..ff5d13e47 --- /dev/null +++ b/src/monkey/mail_sender.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if !defined (__GNUC__) || __GNUC__ < 2 +# define __attribute__(x) +#endif +#define unused __attribute__((unused)) + + +int +handle_invalid_peer_certificate(long vfy_result) +{ + const char *k ="rare error"; + switch(vfy_result) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"; break; + case X509_V_ERR_UNABLE_TO_GET_CRL: + k="X509_V_ERR_UNABLE_TO_GET_CRL"; break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + k="X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"; break; + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + k="X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"; break; + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + k="X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"; break; + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + k="X509_V_ERR_CERT_SIGNATURE_FAILURE"; break; + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + k="X509_V_ERR_CRL_SIGNATURE_FAILURE"; break; + case X509_V_ERR_CERT_NOT_YET_VALID: + k="X509_V_ERR_CERT_NOT_YET_VALID"; break; + case X509_V_ERR_CERT_HAS_EXPIRED: + k="X509_V_ERR_CERT_HAS_EXPIRED"; break; + case X509_V_ERR_CRL_NOT_YET_VALID: + k="X509_V_ERR_CRL_NOT_YET_VALID"; break; + case X509_V_ERR_CRL_HAS_EXPIRED: + k="X509_V_ERR_CRL_HAS_EXPIRED"; break; + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + k="X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"; break; + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + k="X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"; break; + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + k="X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"; break; + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + k="X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"; break; + case X509_V_ERR_OUT_OF_MEM: + k="X509_V_ERR_OUT_OF_MEM"; break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + k="X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"; break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + k="X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"; break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"; break; + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + k="X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"; break; + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + k="X509_V_ERR_CERT_CHAIN_TOO_LONG"; break; + case X509_V_ERR_CERT_REVOKED: + k="X509_V_ERR_CERT_REVOKED"; break; + case X509_V_ERR_INVALID_CA: + k="X509_V_ERR_INVALID_CA"; break; + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + k="X509_V_ERR_PATH_LENGTH_EXCEEDED"; break; + case X509_V_ERR_INVALID_PURPOSE: + k="X509_V_ERR_INVALID_PURPOSE"; break; + case X509_V_ERR_CERT_UNTRUSTED: + k="X509_V_ERR_CERT_UNTRUSTED"; break; + case X509_V_ERR_CERT_REJECTED: + k="X509_V_ERR_CERT_REJECTED"; break; + } + printf("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld: %s\n", vfy_result, k); + return 1; /* Accept the problem */ +} + + +void event_cb (smtp_session_t session, int event_no, void *arg,...) +{ + va_list alist; + int *ok; + + va_start(alist, arg); + switch(event_no) { + case SMTP_EV_CONNECT: + case SMTP_EV_MAILSTATUS: + case SMTP_EV_RCPTSTATUS: + case SMTP_EV_MESSAGEDATA: + case SMTP_EV_MESSAGESENT: + case SMTP_EV_DISCONNECT: break; + case SMTP_EV_WEAK_CIPHER: { + int bits; + bits = va_arg(alist, long); ok = va_arg(alist, int*); + printf("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.\n", bits); + *ok = 1; break; + } + case SMTP_EV_STARTTLS_OK: + puts("SMTP_EV_STARTTLS_OK - TLS started here."); break; + case SMTP_EV_INVALID_PEER_CERTIFICATE: { + long vfy_result; + vfy_result = va_arg(alist, long); ok = va_arg(alist, int*); + *ok = handle_invalid_peer_certificate(vfy_result); + break; + } + case SMTP_EV_NO_PEER_CERTIFICATE: { + ok = va_arg(alist, int*); + puts("SMTP_EV_NO_PEER_CERTIFICATE - accepted."); + *ok = 1; break; + } + case SMTP_EV_WRONG_PEER_CERTIFICATE: { + ok = va_arg(alist, int*); + puts("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."); + *ok = 1; break; + } + case SMTP_EV_NO_CLIENT_CERTIFICATE: { + ok = va_arg(alist, int*); + puts("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."); + *ok = 1; break; + } + default: + printf("Got event: %d - ignored.\n", event_no); + } + va_end(alist); +} + + +/* Callback to prnt the recipient status */ +void +print_recipient_status (smtp_recipient_t recipient, + const char *mailbox, void *arg unused) +{ + const smtp_status_t *status; + + status = smtp_recipient_status (recipient); + printf ("%s: %d %s", mailbox, status->code, status->text); +} + + +void sendMail() +{ + smtp_session_t session; + smtp_message_t message; + smtp_recipient_t recipient; + auth_context_t authctx; + const smtp_status_t *status; + struct sigaction sa; + char *host = "localhost:25"; + char *from = "gnunet-monkey"; + char *subject = "e-mail from Libesmtp!"; + const char *recipient_address = "halims@in.tum.de"; + char tempFileName[1000]; + int tempFd; + FILE *fp; + enum notify_flags notify = Notify_SUCCESS | Notify_FAILURE; + + auth_client_init(); + session = smtp_create_session(); + message = smtp_add_message(session); + + /* Ignore sigpipe */ + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGPIPE, &sa, NULL); + + + smtp_set_server(session, host); + smtp_set_eventcb(session, event_cb, NULL); + + /* Set the reverse path for the mail envelope. (NULL is ok) + */ + smtp_set_reverse_path(message, from); + + /* Set the Subject: header. For no reason, we want the supplied subject + to override any subject line in the message headers. */ + if (subject != NULL) { + smtp_set_header(message, "Subject", subject); + smtp_set_header_option(message, "Subject", Hdr_OVERRIDE, 1); + } + + + /* Prepare message */ + memset(tempFileName, 0, sizeof(tempFileName)); + sprintf(tempFileName, "/tmp/messageXXXXXX"); + tempFd = mkstemp(tempFileName); + fp = fdopen(tempFd, "w"); + fprintf(fp, "Hello! This is a test message!\r\n"); + fclose(fp); + fp = fopen(tempFileName, "r"); + smtp_set_message_fp(message, fp); + + + recipient = smtp_add_recipient(message, recipient_address); + + smtp_dsn_set_notify (recipient, notify); + + /* Initiate a connection to the SMTP server and transfer the + message. */ + if (!smtp_start_session(session)) { + char buf[128]; + + fprintf(stderr, "SMTP server problem %s\n", smtp_strerror(smtp_errno(), + buf, sizeof buf)); + } else { + /* Report on the success or otherwise of the mail transfer. + */ + status = smtp_message_transfer_status(message); + printf("%d %s", status->code, (status->text != NULL) ? status->text + : "\n"); + smtp_enumerate_recipients(message, print_recipient_status, NULL); + } + + /* Free resources consumed by the program. + */ + smtp_destroy_session(session); + auth_destroy_context(authctx); + fclose(fp); + auth_client_exit(); + exit(0); +} + +int main() +{ + sendMail(); + return 0; +} diff --git a/src/monkey/mi_gdb.h b/src/monkey/mi_gdb.h new file mode 100644 index 000000000..df932b4ec --- /dev/null +++ b/src/monkey/mi_gdb.h @@ -0,0 +1,972 @@ +/**[txh]******************************************************************** + + Copyright (c) 2004-2009 by Salvador E. Tropea. + Covered by the GPL license. + + Comments: + Main header for libmigdb. + +***************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include /* pid_t */ + +#define MI_OK 0 +#define MI_OUT_OF_MEMORY 1 +#define MI_PIPE_CREATE 2 +#define MI_FORK 3 +#define MI_DEBUGGER_RUN 4 +#define MI_PARSER 5 +#define MI_UNKNOWN_ASYNC 6 +#define MI_UNKNOWN_RESULT 7 +#define MI_FROM_GDB 8 +#define MI_GDB_TIME_OUT 9 +#define MI_GDB_DIED 10 +#define MI_MISSING_XTERM 11 +#define MI_CREATE_TEMPORAL 12 +#define MI_MISSING_GDB 13 +#define MI_LAST_ERROR 13 + +#define MI_R_NONE 0 /* We are no waiting any response. */ +#define MI_R_SKIP 1 /* We want to discard it. */ +#define MI_R_FE_AND_S 2 /* Wait for done. */ +#define MI_R_E_ARGS 3 + +enum mi_val_type { t_const, t_tuple, t_list }; + +/* Types and subtypes. */ +/* Type. */ +#define MI_T_OUT_OF_BAND 0 +#define MI_T_RESULT_RECORD 1 +/* Out of band subtypes. */ +#define MI_ST_ASYNC 0 +#define MI_ST_STREAM 1 +/* Async sub-subtypes. */ +#define MI_SST_EXEC 0 +#define MI_SST_STATUS 1 +#define MI_SST_NOTIFY 2 +/* Stream sub-subtypes. */ +#define MI_SST_CONSOLE 3 +#define MI_SST_TARGET 4 +#define MI_SST_LOG 5 +/* Classes. */ +/* Async classes. */ +#define MI_CL_UNKNOWN 0 +#define MI_CL_STOPPED 1 +#define MI_CL_DOWNLOAD 2 +/* Result classes. */ +#define MI_CL_DONE 2 +#define MI_CL_RUNNING 3 +#define MI_CL_CONNECTED 4 +#define MI_CL_ERROR 5 +#define MI_CL_EXIT 6 + +#define MI_DEFAULT_TIME_OUT 10 + +#define MI_DIS_ASM 0 +#define MI_DIS_SRC_ASM 1 + +/* Implemented workaround for gdb bugs that we can dis/enable. */ +/* At least gdb<=6.1.1 fails to find a source file with absolute path if the + name is for a psym instead of a sym. psym==partially loaded symbol table. */ +#define MI_PSYM_SEARCH 0 + +#define MI_VERSION_STR "0.8.12" +#define MI_VERSION_MAJOR 0 +#define MI_VERSION_MIDDLE 8 +#define MI_VERSION_MINOR 12 + +struct mi_results_struct +{ + char *var; /* Result name or NULL if just a value. */ + enum mi_val_type type; + union + { + char *cstr; + struct mi_results_struct *rs; + } v; + struct mi_results_struct *next; +}; +typedef struct mi_results_struct mi_results; + +struct mi_output_struct +{ + /* Type of output. */ + char type; + char stype; + char sstype; + char tclass; + /* Content. */ + mi_results *c; + /* Always modeled as a list. */ + struct mi_output_struct *next; +}; +typedef struct mi_output_struct mi_output; + +typedef void (*stream_cb)(const char *, void *); +typedef void (*async_cb)(mi_output *o, void *); +typedef int (*tm_cb)(void *); + +/* Values of this structure shouldn't be manipulated by the user. */ +struct mi_h_struct +{ + /* Pipes connected to gdb. */ + int to_gdb[2]; + int from_gdb[2]; + /* Streams for the pipes. */ + FILE *to, *from; + /* PID of child gdb. */ + pid_t pid; + char died; + /* Which rensponse we are waiting for. */ + /*int response;*/ + /* The line we are reading. */ + char *line; + int llen, lread; + /* Parsed output. */ + mi_output *po, *last; + /* Tunneled streams callbacks. */ + stream_cb console; + void *console_data; + stream_cb target; + void *target_data; + stream_cb log; + void *log_data; + /* Async responses callback. */ + async_cb async; + void *async_data; + /* Callbacks to get echo of gdb dialog. */ + stream_cb to_gdb_echo; + void *to_gdb_echo_data; + stream_cb from_gdb_echo; + void *from_gdb_echo_data; + /* Time out */ + tm_cb time_out_cb; + void *time_out_cb_data; + int time_out; + /* Ugly workaround for some of the show responses :-( */ + int catch_console; + char *catched_console; + /* MI version, currently unknown but the user can force v2 */ + unsigned version; +}; +typedef struct mi_h_struct mi_h; + +#define MI_TO(a) ((a)->to_gdb[1]) + +enum mi_bkp_type { t_unknown=0, t_breakpoint=1, t_hw=2 }; +enum mi_bkp_disp { d_unknown=0, d_keep=1, d_del=2 }; +enum mi_bkp_mode { m_file_line=0, m_function=1, m_file_function=2, m_address=3 }; + +struct mi_bkpt_struct +{ + int number; + enum mi_bkp_type type; + enum mi_bkp_disp disp; /* keep or del if temporal */ + char enabled; + void *addr; + char *func; + char *file; + int line; + int ignore; + int times; + + /* For the user: */ + char *cond; + char *file_abs; + int thread; + enum mi_bkp_mode mode; + struct mi_bkpt_struct *next; +}; +typedef struct mi_bkpt_struct mi_bkpt; + +enum mi_wp_mode { wm_unknown=0, wm_write=1, wm_read=2, wm_rw=3 }; + +struct mi_wp_struct +{ + int number; + char *exp; + enum mi_wp_mode mode; + + /* For the user: */ + struct mi_wp_struct *next; + char enabled; +}; +typedef struct mi_wp_struct mi_wp; + +struct mi_frames_struct +{ + int level; /* The frame number, 0 being the topmost frame, i.e. the innermost + function. */ + void *addr; /* The `$pc' value for that frame. */ + char *func; /* Function name. */ + char *file; /* File name of the source file where the function lives. */ + char *from; + int line; /* Line number corresponding to the `$pc'. */ + /* When arguments are available: */ + mi_results *args; + int thread_id; + /* When more than one is provided: */ + struct mi_frames_struct *next; +}; +typedef struct mi_frames_struct mi_frames; + +struct mi_aux_term_struct +{ + pid_t pid; + char *tty; +}; +typedef struct mi_aux_term_struct mi_aux_term; + +struct mi_pty_struct +{ + char *slave; + int master; +}; +typedef struct mi_pty_struct mi_pty; + +enum mi_gvar_fmt { fm_natural=0, fm_binary=1, fm_decimal=2, fm_hexadecimal=3, + fm_octal=4, + /* Only for registers format: */ + fm_raw=5 }; +enum mi_gvar_lang { lg_unknown=0, lg_c, lg_cpp, lg_java }; + +#define MI_ATTR_DONT_KNOW 0 +#define MI_ATTR_NONEDITABLE 1 +#define MI_ATTR_EDITABLE 2 + +struct mi_gvar_struct +{ + char *name; + int numchild; + char *type; + enum mi_gvar_fmt format; + enum mi_gvar_lang lang; + char *exp; + int attr; + + /* MI v2 fills it, not yet implemented here. */ + /* Use gmi_var_evaluate_expression. */ + char *value; + + /* Pointer to the parent. NULL if none. */ + struct mi_gvar_struct *parent; + /* List containing the children. + Filled by gmi_var_list_children. + NULL if numchild==0 or not yet filled. */ + struct mi_gvar_struct *child; + /* Next var in the list. */ + struct mi_gvar_struct *next; + + /* For the user: */ + char opened; /* We will show its children. 1 when we fill "child" */ + char changed; /* Needs to be updated. 0 when created. */ + int vischild; /* How many items visible. numchild when we fill "child" */ + int depth; /* How deep is this var. */ + char ispointer; +}; +typedef struct mi_gvar_struct mi_gvar; + +struct mi_gvar_chg_struct +{ + char *name; + int in_scope; /* if true the other fields apply. */ + char *new_type; /* NULL if type_changed==false */ + int new_num_children; /* only when new_type!=NULL */ + + struct mi_gvar_chg_struct *next; +}; +typedef struct mi_gvar_chg_struct mi_gvar_chg; + + +/* A list of assembler instructions. */ +struct mi_asm_insn_struct +{ + void *addr; + char *func; + unsigned offset; + char *inst; + + struct mi_asm_insn_struct *next; +}; +typedef struct mi_asm_insn_struct mi_asm_insn; + +/* A list of source lines containing assembler instructions. */ +struct mi_asm_insns_struct +{ + char *file; + int line; + mi_asm_insn *ins; + + struct mi_asm_insns_struct *next; +}; +typedef struct mi_asm_insns_struct mi_asm_insns; + +/* Changed register. */ +struct mi_chg_reg_struct +{ + int reg; + char *val; + char *name; + char updated; + + struct mi_chg_reg_struct *next; +}; +typedef struct mi_chg_reg_struct mi_chg_reg; + +/* + Examining gdb sources and looking at docs I can see the following "stop" +reasons: + +Breakpoints: +a) breakpoint-hit (bkptno) + frame +Also: without reason for temporal breakpoints. + +Watchpoints: +b) watchpoint-trigger (wpt={number,exp};value={old,new}) + frame +c) read-watchpoint-trigger (hw-rwpt{number,exp};value={value}) + frame +d) access-watchpoint-trigger (hw-awpt{number,exp};value={[old,]new}) + frame +e) watchpoint-scope (wpnum) + frame + +Movement: +f) function-finished ([gdb-result-var,return-value]) + frame +g) location-reached + frame +h) end-stepping-range + frame + +Exit: +i) exited-signalled (signal-name,signal-meaning) +j) exited (exit-code) +k) exited-normally + +Signal: +l) signal-received (signal-name,signal-meaning) + frame + +Plus: thread-id +*/ +enum mi_stop_reason +{ + sr_unknown=0, + sr_bkpt_hit, + sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, + sr_function_finished, sr_location_reached, sr_end_stepping_range, + sr_exited_signalled, sr_exited, sr_exited_normally, + sr_signal_received +}; + +struct mi_stop_struct +{ + enum mi_stop_reason reason; /* If more than one reason just the last. */ + /* Flags indicating if non-pointer fields are filled. */ + char have_thread_id; + char have_bkptno; + char have_exit_code; + char have_wpno; + /* Where stopped. Doesn't exist for sr_exited*. */ + int thread_id; + mi_frames *frame; + /* sr_bkpt_hit */ + int bkptno; + /* sr_*wp_* no scope */ + mi_wp *wp; + char *wp_old; + char *wp_val; + /* sr_wp_scope */ + int wpno; + /* sr_function_finished. Not for void func. */ + char *gdb_result_var; + char *return_value; + /* sr_exited_signalled, sr_signal_received */ + char *signal_name; + char *signal_meaning; + /* sr_exited */ + int exit_code; +}; +typedef struct mi_stop_struct mi_stop; + +/* Variable containing the last error. */ +extern int mi_error; +extern char *mi_error_from_gdb; +const char *mi_get_error_str(); + +/* Indicate the name of gdb exe. Default is /usr/bin/gdb */ +void mi_set_gdb_exe(const char *name); +const char *mi_get_gdb_exe(); +/* Indicate the name of a file containing commands to send at start-up */ +void mi_set_gdb_start(const char *name); +const char *mi_get_gdb_start(); +/* Indicate the name of a file containing commands to send after connection */ +void mi_set_gdb_conn(const char *name); +const char *mi_get_gdb_conn(); +void mi_send_target_commands(mi_h *h); +/* Connect to a local copy of gdb. */ +mi_h *mi_connect_local(); +/* Close connection. You should ask gdb to quit first. */ +void mi_disconnect(mi_h *h); +/* Force MI version. */ +#define MI_VERSION2U(maj,mid,min) (maj*0x1000000+mid*0x10000+min) +void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, + unsigned vMinor); +void mi_set_workaround(unsigned wa, int enable); +int mi_get_workaround(unsigned wa); +/* Parse gdb output. */ +mi_output *mi_parse_gdb_output(const char *str); +/* Functions to set/get the tunneled streams callbacks. */ +void mi_set_console_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_target_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_log_cb(mi_h *h, stream_cb cb, void *data); +stream_cb mi_get_console_cb(mi_h *h, void **data); +stream_cb mi_get_target_cb(mi_h *h, void **data); +stream_cb mi_get_log_cb(mi_h *h, void **data); +/* The callback to deal with async events. */ +void mi_set_async_cb(mi_h *h, async_cb cb, void *data); +async_cb mi_get_async_cb(mi_h *h, void **data); +/* Time out in gdb responses. */ +void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data); +tm_cb mi_get_time_out_cb(mi_h *h, void **data); +void mi_set_time_out(mi_h *h, int to); +int mi_get_time_out(mi_h *h); +/* Callbacks to "see" the dialog with gdb. */ +void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data); +void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data); +stream_cb mi_get_to_gdb_cb(mi_h *h, void **data); +stream_cb mi_get_from_gdb_cb(mi_h *h, void **data); +/* Sends a message to gdb. */ +int mi_send(mi_h *h, const char *format, ...); +/* Wait until gdb sends a response. */ +mi_output *mi_get_response_blk(mi_h *h); +/* Check if gdb sent a complete response. Use with mi_retire_response. */ +int mi_get_response(mi_h *h); +/* Get the last response. Use with mi_get_response. */ +mi_output *mi_retire_response(mi_h *h); +/* Look for a result record in gdb output. */ +mi_output *mi_get_rrecord(mi_output *r); +/* Look if the output contains an async stop. + If that's the case return the reason for the stop. + If the output contains an error the description is returned in reason. */ +int mi_get_async_stop_reason(mi_output *r, char **reason); +mi_stop *mi_get_stopped(mi_results *r); +mi_frames *mi_get_async_frame(mi_output *r); +/* Wait until gdb sends a response. + Then check if the response is of the desired type. */ +int mi_res_simple_exit(mi_h *h); +int mi_res_simple_done(mi_h *h); +int mi_res_simple_running(mi_h *h); +int mi_res_simple_connected(mi_h *h); +/* It additionally extracts an specified variable. */ +mi_results *mi_res_done_var(mi_h *h, const char *var); +/* Extract a frames list from the response. */ +mi_frames *mi_res_frames_array(mi_h *h, const char *var); +mi_frames *mi_res_frames_list(mi_h *h); +mi_frames *mi_parse_frame(mi_results *c); +mi_frames *mi_res_frame(mi_h *h); +/* Create an auxiliar terminal using xterm. */ +mi_aux_term *gmi_start_xterm(); +/* Indicate the name of xterm exe. Default is /usr/bin/X11/xterm */ +void mi_set_xterm_exe(const char *name); +const char *mi_get_xterm_exe(); +/* Kill the auxiliar terminal and release the structure. */ +void gmi_end_aux_term(mi_aux_term *t); +/* Look for a free Linux VT for the child. */ +mi_aux_term *gmi_look_for_free_vt(); +/* Look for a free and usable Linux VT. */ +int mi_look_for_free_vt(); +/* Close master and release the structure. */ +void gmi_end_pty(mi_pty *p); +/* Look for a free pseudo terminal. */ +mi_pty *gmi_look_for_free_pty(); +/* Extract a list of thread IDs from response. */ +int mi_res_thread_ids(mi_h *h, int **list); +int mi_get_thread_ids(mi_output *res, int **list); +/* A variable response. */ +mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression); +enum mi_gvar_fmt mi_format_str_to_enum(const char *format); +const char *mi_format_enum_to_str(enum mi_gvar_fmt format); +char mi_format_enum_to_char(enum mi_gvar_fmt format); +enum mi_gvar_lang mi_lang_str_to_enum(const char *lang); +const char *mi_lang_enum_to_str(enum mi_gvar_lang lang); +int mi_res_changelist(mi_h *h, mi_gvar_chg **changed); +int mi_res_children(mi_h *h, mi_gvar *v); +mi_bkpt *mi_res_bkpt(mi_h *h); +mi_wp *mi_res_wp(mi_h *h); +char *mi_res_value(mi_h *h); +mi_stop *mi_res_stop(mi_h *h); +enum mi_stop_reason mi_reason_str_to_enum(const char *s); +const char *mi_reason_enum_to_str(enum mi_stop_reason r); +int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, + unsigned long *addr); +mi_asm_insns *mi_get_asm_insns(mi_h *h); +/* Starting point of the program. */ +void mi_set_main_func(const char *name); +const char *mi_get_main_func(); +mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many); +int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l); +mi_chg_reg *mi_get_list_changed_regs(mi_h *h); +int mi_get_reg_values(mi_h *h, mi_chg_reg *l); +mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many); +int gmi_target_download(mi_h *h); + +/* Allocation functions: */ +void *mi_calloc(size_t count, size_t sz); +void *mi_calloc1(size_t sz); +char *mi_malloc(size_t sz); +mi_results *mi_alloc_results(void); +mi_output *mi_alloc_output(void); +mi_frames *mi_alloc_frames(void); +mi_gvar *mi_alloc_gvar(void); +mi_gvar_chg *mi_alloc_gvar_chg(void); +mi_bkpt *mi_alloc_bkpt(void); +mi_wp *mi_alloc_wp(void); +mi_stop *mi_alloc_stop(void); +mi_asm_insns *mi_alloc_asm_insns(void); +mi_asm_insn *mi_alloc_asm_insn(void); +mi_chg_reg *mi_alloc_chg_reg(void); +void mi_free_output(mi_output *r); +void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r); +void mi_free_frames(mi_frames *f); +void mi_free_aux_term(mi_aux_term *t); +void mi_free_results(mi_results *r); +void mi_free_results_but(mi_results *r, mi_results *no); +void mi_free_gvar(mi_gvar *v); +void mi_free_gvar_chg(mi_gvar_chg *p); +void mi_free_wp(mi_wp *wp); +void mi_free_stop(mi_stop *s); +void mi_free_asm_insns(mi_asm_insns *i); +void mi_free_asm_insn(mi_asm_insn *i); +void mi_free_charp_list(char **l); +void mi_free_chg_reg(mi_chg_reg *r); + +/* Porgram control: */ +/* Specify the executable and arguments for local debug. */ +int gmi_set_exec(mi_h *h, const char *file, const char *args); +/* Start running the executable. Remote sessions starts running. */ +int gmi_exec_run(mi_h *h); +/* Continue the execution after a "stop". */ +int gmi_exec_continue(mi_h *h); +/* Indicate which terminal will use the target program. For local sessions. */ +int gmi_target_terminal(mi_h *h, const char *tty_name); +/* Specify what's the local copy that have debug info. For remote sessions. */ +int gmi_file_symbol_file(mi_h *h, const char *file); +/* Continue until function return, the return value is included in the async + response. */ +int gmi_exec_finish(mi_h *h); +/* Stop the program using SIGINT. */ +int gmi_exec_interrupt(mi_h *h); +/* Next line of code. */ +int gmi_exec_next(mi_h *h); +/* Next count lines of code. */ +int gmi_exec_next_cnt(mi_h *h, int count); +/* Next line of assembler code. */ +int gmi_exec_next_instruction(mi_h *h); +/* Next line of code. Get inside functions. */ +int gmi_exec_step(mi_h *h); +/* Next count lines of code. Get inside functions. */ +int gmi_exec_step_cnt(mi_h *h, int count); +/* Next line of assembler code. Get inside calls. */ +int gmi_exec_step_instruction(mi_h *h); +/* Execute until location is reached. If file is NULL then is until next line. */ +int gmi_exec_until(mi_h *h, const char *file, int line); +int gmi_exec_until_addr(mi_h *h, void *addr); +/* Return to previous frame inmediatly. */ +mi_frames *gmi_exec_return(mi_h *h); +/* Just kill the program. Please read the notes in prg_control.c. */ +int gmi_exec_kill(mi_h *h); + +/* Target manipulation: */ +/* Connect to a remote gdbserver using the specified methode. */ +int gmi_target_select(mi_h *h, const char *type, const char *params); +/* Attach to an already running process. */ +mi_frames *gmi_target_attach(mi_h *h, pid_t pid); +/* Detach from an attached process. */ +int gmi_target_detach(mi_h *h); + +/* Miscellaneous commands: */ +/* Exit gdb killing the child is it is running. */ +void gmi_gdb_exit(mi_h *h); +/* Send the version to the console. */ +int gmi_gdb_version(mi_h *h); +/* Set a gdb variable. */ +int gmi_gdb_set(mi_h *h, const char *var, const char *val); +/* Get a gdb variable. */ +char *gmi_gdb_show(mi_h *h, const char *var); + +/* Breakpoints manipulation: */ +/* Insert a breakpoint at file:line. */ +mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line); +/* Insert a breakpoint, all available options. */ +mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, + const char *cond, int count, int thread, + const char *where); +mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line, + int temporary, int hard_assist, + const char *cond, int count, int thread); +/* Remove a breakpoint. */ +int gmi_break_delete(mi_h *h, int number); +/* Free the memory used for a breakpoint description. */ +void mi_free_bkpt(mi_bkpt *b); +/* Modify the "ignore" count for a breakpoint. */ +int gmi_break_set_times(mi_h *h, int number, int count); +/* Associate a condition with the breakpoint. */ +int gmi_break_set_condition(mi_h *h, int number, const char *condition); +/* Enable or disable a breakpoint. */ +int gmi_break_state(mi_h *h, int number, int enable); +/* Set a watchpoint. It doesn't work for remote targets! */ +mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp); + +/* Data Manipulation. */ +/* Evaluate an expression. Returns a parsed tree. */ +char *gmi_data_evaluate_expression(mi_h *h, const char *expression); +/* Path for sources. */ +int gmi_dir(mi_h *h, const char *path); +/* A very limited "data read memory" implementation. */ +int gmi_read_memory(mi_h *h, const char *exp, unsigned size, + unsigned char *dest, int *na, int convAddr, + unsigned long *addr); +mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, + const char *end, int mode); +mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, + int lines, int mode); +mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many); +int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l); +mi_chg_reg *gmi_data_list_changed_registers(mi_h *h); +int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l); +mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many); + +/* Stack manipulation. */ +/* List of frames. Arguments aren't filled. */ +mi_frames *gmi_stack_list_frames(mi_h *h); +/* List of frames. Indicating a range. */ +mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to); +/* List arguments. Only level and args filled. */ +mi_frames *gmi_stack_list_arguments(mi_h *h, int show); +/* List arguments. Indicating a range. Only level and args filled. */ +mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to); +/* Information about the current frame, including args. */ +mi_frames *gmi_stack_info_frame(mi_h *h); +/* Stack info depth. error => -1 */ +int gmi_stack_info_depth_get(mi_h *h); +/* Set stack info depth. error => -1 */ +int gmi_stack_info_depth(mi_h *h, int max_depth); +/* Change current frame. */ +int gmi_stack_select_frame(mi_h *h, int framenum); +/* List of local vars. */ +mi_results *gmi_stack_list_locals(mi_h *h, int show); + +/* Thread. */ +/* List available thread ids. */ +int gmi_thread_list_ids(mi_h *h, int **list); +/* Select a thread. */ +mi_frames *gmi_thread_select(mi_h *h, int id); +/* List available threads. */ +mi_frames *gmi_thread_list_all_threads(mi_h *h); + +/* Variable objects. */ +/* Create a variable object. */ +mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp); +mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp); +/* Create the variable and also fill the lang and attr fields. */ +mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp); +/* Delete a variable object. Doesn't free the mi_gvar data. */ +int gmi_var_delete(mi_h *h, mi_gvar *var); +/* Set the format used to represent the result. */ +int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format); +/* Fill the format field with info from gdb. */ +int gmi_var_show_format(mi_h *h, mi_gvar *var); +/* Fill the numchild field with info from gdb. */ +int gmi_var_info_num_children(mi_h *h, mi_gvar *var); +/* Fill the type field with info from gdb. */ +int gmi_var_info_type(mi_h *h, mi_gvar *var); +/* Fill the expression and lang fields with info from gdb. + Note that lang isn't filled during creation. */ +int gmi_var_info_expression(mi_h *h, mi_gvar *var); +/* Fill the attr field with info from gdb. + Note that attr isn't filled during creation. */ +int gmi_var_show_attributes(mi_h *h, mi_gvar *var); +/* Update variable. Use NULL for all. + Note that *changed can be NULL if none updated. */ +int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed); +/* Change variable. Fills the value field. */ +int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression); +/* Get current value for a variable. */ +int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var); +/* List children. It ONLY returns the first level information. :-( */ +int gmi_var_list_children(mi_h *h, mi_gvar *var); + +#ifdef __cplusplus +}; + +/* C++ interface */ + +/* + State Can: + disconnected Connect + connected SelectTarget, Disconnect + target_specified TargetUnselect, Run, Set breakpoints/watchpoints, etc. + running Stop + stopped Kill, Restart?, Step, Trace, Continue, etc. + [auto exit] + + Modes: + dmX11 Local debug for X11. + dmLinux Local debug for Linux console. + dmRemote Remote debug. +*/ +class MIDebugger +{ +public: + MIDebugger(); + ~MIDebugger(); + + enum eState { disconnected, connected, target_specified, running, stopped }; + enum dMode { dmX11, dmLinux, dmRemote, dmPID }; + enum endianType { enUnknown, enLittle, enBig }; + // Currently tested architectures + enum archType { arUnknown, arIA32, arSPARC, arPIC14, arAVR, arUnsupported }; + + int Connect(bool remote=false); /* remote is currently ignored. */ + int Disconnect(); + /* SelectTarget* */ + int SelectTargetX11(const char *exec, const char *args=NULL, + const char *auxtty=NULL); + int SelectTargetLinux(const char *exec, const char *args, + const char *auxtty=NULL); + int SelectTargetRemote(const char *exec, const char *rparams, + const char *rtype=NULL, bool download=false); + // TODO: Linux PIDs can be represented as intergers. What should I use? + // ato_pid_t doesn't exist ;-) + mi_frames *SelectTargetPID(const char *exec, int pid); + int TargetUnselect(); + int Run(); + int Stop(); + int Poll(mi_stop *&rs); + int Continue(); + int RunOrContinue(); + int Kill(); + mi_bkpt *Breakpoint(const char *file, int line); + mi_bkpt *Breakpoint(const char *where, bool temporary=false, const char *cond=NULL, + int count=-1, int thread=-1, bool hard_assist=false); + mi_bkpt *BreakpointFull(const char *file, int line, bool temporary=false, + const char *cond=NULL, int count=-1, int thread=-1, + bool hard_assist=false); + mi_bkpt *Breakpoint(mi_bkpt *b); + int BreakDelete(mi_bkpt *b); + int BreakAfter(mi_bkpt *b) + { + if (state!=target_specified && state!=stopped) + return 0; + return gmi_break_set_times(h,b->number,b->ignore); + } + mi_wp *Watchpoint(enum mi_wp_mode mode, const char *exp); + int WatchDelete(mi_wp *w); + int RunToMain(); + int StepOver(bool inst=false); + int TraceInto(bool inst=false); + int GoTo(const char *file, int line); + int GoTo(void *addr); + int FinishFun(); + mi_frames *ReturnNow(); + mi_frames *CallStack(bool args); + char *EvalExpression(const char *exp); + char *ModifyExpression(char *exp, char *newVal); + mi_gvar *AddgVar(const char *exp, int frame=-1) + { + if (state!=stopped) + return NULL; + return gmi_full_var_create(h,frame,exp); + } + int DelgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_delete(h,var); + } + int EvalgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_evaluate_expression(h,var); + } + int GetChildgVar(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_list_children(h,var); + } + int FillTypeVal(mi_gvar *var); + int FillOneTypeVal(mi_gvar *var); + int FillAttr(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_show_attributes(h,var); + } + int FillFormat(mi_gvar *var) + { + if (state!=stopped) + return 0; + return gmi_var_show_format(h,var); + } + int SetFormatgVar(mi_gvar *var, enum mi_gvar_fmt format) + { + if (state!=stopped) + return 0; + return gmi_var_set_format(h,var,format); + } + int ListChangedgVar(mi_gvar_chg *&changed) + { + if (state!=stopped) + return 0; + return gmi_var_update(h,NULL,&changed); + } + int AssigngVar(mi_gvar *var, const char *exp); + int Send(const char *command); + int Version() + { + if (state==running || state==disconnected) + return 0; + return gmi_gdb_version(h); + } + int PathSources(const char *path) + { + if (state==running || state==disconnected) + return 0; + return gmi_dir(h,path); + } + int ReadMemory(const char *exp, unsigned size, unsigned char *dest, + int &na, int convAddr, unsigned long *addr) + { + if (state!=stopped) + return 0; + return gmi_read_memory(h,exp,size,dest,&na,convAddr,addr); + } + char *Show(const char *var); + int ThreadListIDs(int *&list) + { + if (state!=stopped) + return 0; + return gmi_thread_list_ids(h,&list); + } + mi_frames *ThreadList() + { + if (state!=stopped) + return 0; + return gmi_thread_list_all_threads(h); + } + mi_frames *ThreadSelect(int id) + { + if (state!=stopped) + return NULL; + return gmi_thread_select(h,id); + } + mi_asm_insns *Disassemble(const char *start, const char *end, int mode) + { + if (state!=stopped) + return NULL; + return gmi_data_disassemble_se(h,start,end,mode); + } + mi_asm_insns *Disassemble(const char *file, int line, int lines, int mode) + { + if (state!=stopped) + return NULL; + return gmi_data_disassemble_fl(h,file,line,lines,mode); + } + mi_chg_reg *GetRegisterNames(int *how_many) + { + if (state!=target_specified && state!=stopped) + return NULL; + return gmi_data_list_register_names(h,how_many); + } + int GetRegisterNames(mi_chg_reg *chg) + { + if (state!=target_specified && state!=stopped) + return 0; + return gmi_data_list_register_names_l(h,chg); + } + int GetRegisterValues(mi_chg_reg *chg) + { + if (state!=stopped) + return 0; + return gmi_data_list_register_values(h,fm_natural,chg); + } + mi_chg_reg *GetRegisterValues(int *how_many) + { + if (state!=stopped) + return 0; + return gmi_data_list_all_register_values(h,fm_natural,how_many); + } + mi_chg_reg *GetChangedRegisters() + { + if (state!=stopped) + return NULL; + mi_chg_reg *chg=gmi_data_list_changed_registers(h); + if (chg && !gmi_data_list_register_values(h,fm_natural,chg)) + { + mi_free_chg_reg(chg); + chg=NULL; + } + return chg; + } + int UpdateRegisters(mi_chg_reg *regs); + + endianType GetTargetEndian(); + archType GetTargetArchitecture(); + eState GetState() { return state; } + + /* Some wrappers */ + static void SetGDBExe(const char *name) { mi_set_gdb_exe(name); } + static const char *GetGDBExe() { return mi_get_gdb_exe(); } + static void SetXTermExe(const char *name) { mi_set_xterm_exe(name); } + static const char *GetXTermExe() { return mi_get_xterm_exe(); } + static void SetGDBStartFile(const char *name) { mi_set_gdb_start(name); } + static const char *GetGDBStartFile() { return mi_get_gdb_start(); } + static void SetGDBConnFile(const char *name) { mi_set_gdb_conn(name); } + static const char *GetGDBConnFile() { return mi_get_gdb_conn(); } + static void SetMainFunc(const char *name) { mi_set_main_func(name); } + static const char *GetMainFunc() { return mi_get_main_func(); } + + static const char *GetErrorStr() { return mi_get_error_str(); } + static const char *GetGDBError() { return mi_error_from_gdb; } + static int GetErrorNumber() { return mi_error; } + int GetErrorNumberSt(); + void SetConsoleCB(stream_cb cb, void *data=NULL) + { mi_set_console_cb(h,cb,data); } + void SetTargetCB(stream_cb cb, void *data=NULL) + { mi_set_target_cb(h,cb,data); } + void SetLogCB(stream_cb cb, void *data=NULL) + { mi_set_log_cb(h,cb,data); } + void SetAsyncCB(async_cb cb, void *data=NULL) + { mi_set_async_cb(h,cb,data); } + void SetToGDBCB(stream_cb cb, void *data=NULL) + { mi_set_to_gdb_cb(h,cb,data); } + void SetFromGDBCB(stream_cb cb, void *data=NULL) + { mi_set_from_gdb_cb(h,cb,data); } + void SetTimeOutCB(tm_cb cb, void *data) + { mi_set_time_out_cb(h,cb,data); } + void SetTimeOut(int to) + { mi_set_time_out(h,to); } + void ForceMIVersion(unsigned vMajor, unsigned vMiddle, unsigned vMinor) + { mi_force_version(h,vMajor,vMiddle,vMinor); } + + const char *GetAuxTTY() + { return aux_tty ? aux_tty->tty : NULL; } + +protected: + eState state; + dMode mode; + endianType targetEndian; + archType targetArch; + bool preRun; // Remote targets starts running but outside main. + mi_h *h; + mi_aux_term *aux_tty; + int waitingTempBkpt; + + int SelectTargetTTY(const char *exec, const char *args, const char *auxtty, + dMode m); +}; + +#endif + diff --git a/src/monkey/misc.c b/src/monkey/misc.c new file mode 100644 index 000000000..5440ab0f0 --- /dev/null +++ b/src/monkey/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/monkey.h b/src/monkey/monkey.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/monkey/monkey_api.c b/src/monkey/monkey_api.c new file mode 100644 index 000000000..e69de29bb diff --git a/src/monkey/parse.c b/src/monkey/parse.c new file mode 100644 index 000000000..0dea6cb5d --- /dev/null +++ b/src/monkey/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/prg_control.c b/src/monkey/prg_control.c new file mode 100644 index 000000000..671725f94 --- /dev/null +++ b/src/monkey/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/stack_man.c b/src/monkey/stack_man.c new file mode 100644 index 000000000..8e8ed0691 --- /dev/null +++ b/src/monkey/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/symbol_query.c b/src/monkey/symbol_query.c new file mode 100644 index 000000000..55e145f87 --- /dev/null +++ b/src/monkey/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/target_man.c b/src/monkey/target_man.c new file mode 100644 index 000000000..cf6c815b2 --- /dev/null +++ b/src/monkey/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/test_gnunet_monkey.sh b/src/monkey/test_gnunet_monkey.sh new file mode 100755 index 000000000..7595a68fb --- /dev/null +++ b/src/monkey/test_gnunet_monkey.sh @@ -0,0 +1,199 @@ +#!/bin/sh + +rm -rf /tmp/test-gnunetd-monkey/ +exe="./gnunet-monkey -c test_monkey_api_data.conf" +out=`mktemp /tmp/test-gnunet-monkey-logXXXXXXXX` +arm="gnunet-arm -c test_monkey_api_data.conf $DEBUG" +#DEBUG="-L DEBUG" +# ----------------------------------- +echo -n "Preparing: Starting service..." + +$arm -s > /dev/null +sleep 1 +$arm -i monkey > /dev/null +sleep 1 +echo "DONE" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Bad argument checking..." + +if $exe -x 2> /dev/null; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set value..." + +if ! $exe $DEBUG -n test -s subsystem 42 ; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set another value..." + +if ! $exe $DEBUG -n other -s osystem 43 ; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing all stats..." + +if ! $exe $DEBUG > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | wc -l` +if test $LINES -ne 2; then + echo "FAIL: unexpected output" + $arm -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing stats by name..." + +if ! $exe $DEBUG -n other > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep 43 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + $arm -e + exit 1 +fi +echo "PASS" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: viewing stats by subsystem..." + +if ! $exe $DEBUG -s subsystem > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep 42 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + $arm -e + exit 1 +fi +echo "PASS" + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Set persistent value..." + +if ! $exe $DEBUG -n lasting -s subsystem 40 -p; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +if ! $exe $DEBUG > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep 40 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + cat $out + $arm -e + exit 1 +fi +echo "PASS" + +# ----------------------------------- +echo -n "Restarting service..." +$arm -k monkey > /dev/null +sleep 1 +$arm -i monkey > /dev/null +sleep 1 +echo "DONE" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: checking persistence..." + +if ! $exe $DEBUG > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep 40 | wc -l` +if test $LINES -ne 1; then + echo "FAIL: unexpected output" + cat $out + $arm -e + exit 1 +fi +echo "PASS" + + + +# ---------------------------------------------------------------------------------- +echo -n "TEST: Removing persistence..." + +if ! $exe $DEBUG -n lasting -s subsystem 40; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +if ! $exe $DEBUG > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep \! | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + cat $out + $arm -e + exit 1 +fi +echo "PASS" + + +# ----------------------------------- +echo -n "Restarting service..." +$arm -k monkey > /dev/null +sleep 1 +$arm -i monkey > /dev/null +sleep 1 +echo "DONE" + +# ---------------------------------------------------------------------------------- +echo -n "TEST: checking removed persistence..." + +if ! $exe $DEBUG > $out; then + echo "FAIL: error running $exe" + $arm -e + exit 1 +fi +LINES=`cat $out | grep 40 | wc -l` +if test $LINES -ne 0; then + echo "FAIL: unexpected output" + cat $out + $arm -e + exit 1 +fi +echo "PASS" + +# ----------------------------------- +echo -n "Stopping service..." +$arm -e > /dev/null +sleep 1 +echo "DONE" +rm -f $out +rm -rf /tmp/test-gnunetd-monkey/ diff --git a/src/monkey/test_monkey_api.c b/src/monkey/test_monkey_api.c new file mode 100644 index 000000000..d63453bee --- /dev/null +++ b/src/monkey/test_monkey_api.c @@ -0,0 +1,6 @@ + + +int main(int argc, char *argv[]) +{ + return 0; +} \ No newline at end of file diff --git a/src/monkey/thread.c b/src/monkey/thread.c new file mode 100644 index 000000000..51a333f5b --- /dev/null +++ b/src/monkey/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/var_obj.c b/src/monkey/var_obj.c new file mode 100644 index 000000000..a027d6751 --- /dev/null +++ b/src/monkey/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); +} + -- cgit v1.2.3