diff options
29 files changed, 8184 insertions, 0 deletions
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 | |||
673 | src/topology/Makefile | 673 | src/topology/Makefile |
674 | src/transport/Makefile | 674 | src/transport/Makefile |
675 | src/util/Makefile | 675 | src/util/Makefile |
676 | src/monkey/Makefile | ||
676 | ]) | 677 | ]) |
677 | AC_OUTPUT | 678 | AC_OUTPUT |
678 | 679 | ||
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 | |||
6 | NAT_DIR = nat | 6 | NAT_DIR = nat |
7 | endif | 7 | endif |
8 | 8 | ||
9 | if HAVE_ESMTP | ||
10 | MONKEY_DIR = monkey | ||
11 | endif | ||
12 | |||
9 | SUBDIRS = \ | 13 | SUBDIRS = \ |
10 | include $(INTLEMU_SUBDIRS) \ | 14 | include $(INTLEMU_SUBDIRS) \ |
11 | util \ | 15 | util \ |
12 | block \ | 16 | block \ |
13 | statistics \ | 17 | statistics \ |
14 | arm \ | 18 | arm \ |
19 | $(MONKEY_DIR) \ | ||
15 | hello \ | 20 | hello \ |
16 | peerinfo \ | 21 | peerinfo \ |
17 | datacache \ | 22 | 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 @@ | |||
1 | INCLUDES = -I$(top_srcdir)/src/include | ||
2 | |||
3 | if MINGW | ||
4 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
5 | endif | ||
6 | |||
7 | if USE_COVERAGE | ||
8 | AM_CFLAGS = --coverage -O0 | ||
9 | XLIB = -lgcov | ||
10 | endif | ||
11 | |||
12 | |||
13 | lib_LTLIBRARIES = libgnunetmonkey.la | ||
14 | |||
15 | libgnunetmonkey_la_SOURCES = \ | ||
16 | monkey_api.c monkey.h | ||
17 | libgnunetmonkey_la_LIBADD = \ | ||
18 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
19 | -lesmtp | ||
20 | libgnunetmonkey_la_LDFLAGS = \ | ||
21 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
22 | -version-info 0:0:0 | ||
23 | |||
24 | |||
25 | bin_PROGRAMS = \ | ||
26 | gnunet-monkey \ | ||
27 | gnunet-service-monkey \ | ||
28 | bug_null_pointer_exception \ | ||
29 | mail_sender | ||
30 | |||
31 | gnunet_monkey_SOURCES = \ | ||
32 | mi_gdb.h \ | ||
33 | alloc.c \ | ||
34 | breakpoint.c \ | ||
35 | connect.c \ | ||
36 | cpp_int.cc \ | ||
37 | data_man.c \ | ||
38 | error.c \ | ||
39 | get_free_pty.c \ | ||
40 | get_free_vt.c \ | ||
41 | gnunet-monkey.c \ | ||
42 | misc.c \ | ||
43 | parse.c \ | ||
44 | prg_control.c \ | ||
45 | stack_man.c \ | ||
46 | symbol_query.c \ | ||
47 | target_man.c \ | ||
48 | thread.c \ | ||
49 | var_obj.c | ||
50 | |||
51 | gnunet_monkey_LDADD = \ | ||
52 | $(top_builddir)/src/monkey/libgnunetmonkey.la \ | ||
53 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
54 | $(GN_LIBINTL) | ||
55 | |||
56 | |||
57 | |||
58 | gnunet_service_monkey_SOURCES = \ | ||
59 | gnunet-service-monkey.c | ||
60 | gnunet_service_monkey_LDADD = \ | ||
61 | $(top_builddir)/src/monkey/libgnunetmonkey.la \ | ||
62 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
63 | $(GN_LIBINTL) | ||
64 | |||
65 | |||
66 | mail_sender_SOURCES = \ | ||
67 | mail_sender.c | ||
68 | mail_sender_LDADD = \ | ||
69 | $(top_builddir)/src/monkey/libgnunetmonkey.la \ | ||
70 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
71 | $(GN_LIBINTL) | ||
72 | |||
73 | bug_null_pointer_exception_SOURCES = \ | ||
74 | bug_null_pointer_exception.c | ||
75 | bug_null_pointer_exception_LDADD = \ | ||
76 | $(top_builddir)/src/monkey/libgnunetmonkey.la \ | ||
77 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
78 | $(GN_LIBINTL) | ||
79 | |||
80 | |||
81 | |||
82 | check_PROGRAMS = \ | ||
83 | test_monkey_api | ||
84 | |||
85 | TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||
86 | |||
87 | test_monkey_api_SOURCES = \ | ||
88 | test_monkey_api.c | ||
89 | test_monkey_api_LDADD = \ | ||
90 | $(top_builddir)/src/monkey/libgnunetmonkey.la \ | ||
91 | $(top_builddir)/src/util/libgnunetutil.la | ||
92 | |||
93 | check_SCRIPTS = \ | ||
94 | test_gnunet_monkey.sh | ||
95 | |||
96 | EXTRA_DIST = \ | ||
97 | test_monkey_api_data.conf \ | ||
98 | $(check_SCRIPTS) | ||
99 | |||
100 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Allocator. | ||
7 | Comments: | ||
8 | Most alloc/free routines are here. Free routines must accept NULL | ||
9 | pointers. Alloc functions must set mi_error.@p | ||
10 | |||
11 | ***************************************************************************/ | ||
12 | |||
13 | #include "mi_gdb.h" | ||
14 | |||
15 | void *mi_calloc(size_t count, size_t sz) | ||
16 | { | ||
17 | void *res=calloc(count,sz); | ||
18 | if (!res) | ||
19 | mi_error=MI_OUT_OF_MEMORY; | ||
20 | return res; | ||
21 | } | ||
22 | |||
23 | void *mi_calloc1(size_t sz) | ||
24 | { | ||
25 | return mi_calloc(1,sz); | ||
26 | } | ||
27 | |||
28 | char *mi_malloc(size_t sz) | ||
29 | { | ||
30 | char *res=malloc(sz); | ||
31 | if (!res) | ||
32 | mi_error=MI_OUT_OF_MEMORY; | ||
33 | return res; | ||
34 | } | ||
35 | |||
36 | mi_results *mi_alloc_results(void) | ||
37 | { | ||
38 | return (mi_results *)mi_calloc1(sizeof(mi_results)); | ||
39 | } | ||
40 | |||
41 | mi_output *mi_alloc_output(void) | ||
42 | { | ||
43 | return (mi_output *)mi_calloc1(sizeof(mi_output)); | ||
44 | } | ||
45 | |||
46 | mi_frames *mi_alloc_frames(void) | ||
47 | { | ||
48 | return (mi_frames *)mi_calloc1(sizeof(mi_frames)); | ||
49 | } | ||
50 | |||
51 | mi_gvar *mi_alloc_gvar(void) | ||
52 | { | ||
53 | return (mi_gvar *)mi_calloc1(sizeof(mi_gvar)); | ||
54 | } | ||
55 | |||
56 | mi_gvar_chg *mi_alloc_gvar_chg(void) | ||
57 | { | ||
58 | return (mi_gvar_chg *)mi_calloc1(sizeof(mi_gvar_chg)); | ||
59 | } | ||
60 | |||
61 | mi_bkpt *mi_alloc_bkpt(void) | ||
62 | { | ||
63 | mi_bkpt *b=(mi_bkpt *)mi_calloc1(sizeof(mi_bkpt)); | ||
64 | if (b) | ||
65 | { | ||
66 | b->thread=-1; | ||
67 | b->ignore=-1; | ||
68 | } | ||
69 | return b; | ||
70 | } | ||
71 | |||
72 | mi_wp *mi_alloc_wp(void) | ||
73 | { | ||
74 | return (mi_wp *)mi_calloc1(sizeof(mi_wp)); | ||
75 | } | ||
76 | |||
77 | mi_stop *mi_alloc_stop(void) | ||
78 | { | ||
79 | return (mi_stop *)mi_calloc1(sizeof(mi_stop)); | ||
80 | } | ||
81 | |||
82 | mi_asm_insns *mi_alloc_asm_insns(void) | ||
83 | { | ||
84 | return (mi_asm_insns *)mi_calloc1(sizeof(mi_asm_insns)); | ||
85 | } | ||
86 | |||
87 | mi_asm_insn *mi_alloc_asm_insn(void) | ||
88 | { | ||
89 | return (mi_asm_insn *)mi_calloc1(sizeof(mi_asm_insn)); | ||
90 | } | ||
91 | |||
92 | mi_chg_reg *mi_alloc_chg_reg(void) | ||
93 | { | ||
94 | return (mi_chg_reg *)mi_calloc1(sizeof(mi_chg_reg)); | ||
95 | } | ||
96 | |||
97 | /***************************************************************************** | ||
98 | Free functions | ||
99 | *****************************************************************************/ | ||
100 | |||
101 | void mi_free_frames(mi_frames *f) | ||
102 | { | ||
103 | mi_frames *aux; | ||
104 | |||
105 | while (f) | ||
106 | { | ||
107 | free(f->func); | ||
108 | free(f->file); | ||
109 | free(f->from); | ||
110 | mi_free_results(f->args); | ||
111 | aux=f->next; | ||
112 | free(f); | ||
113 | f=aux; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | void mi_free_bkpt(mi_bkpt *b) | ||
118 | { | ||
119 | mi_bkpt *aux; | ||
120 | |||
121 | while (b) | ||
122 | { | ||
123 | free(b->func); | ||
124 | free(b->file); | ||
125 | free(b->file_abs); | ||
126 | free(b->cond); | ||
127 | aux=b->next; | ||
128 | free(b); | ||
129 | b=aux; | ||
130 | } | ||
131 | } | ||
132 | |||
133 | void mi_free_gvar(mi_gvar *v) | ||
134 | { | ||
135 | mi_gvar *aux; | ||
136 | |||
137 | while (v) | ||
138 | { | ||
139 | free(v->name); | ||
140 | free(v->type); | ||
141 | free(v->exp); | ||
142 | free(v->value); | ||
143 | if (v->numchild && v->child) | ||
144 | mi_free_gvar(v->child); | ||
145 | aux=v->next; | ||
146 | free(v); | ||
147 | v=aux; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | void mi_free_gvar_chg(mi_gvar_chg *p) | ||
152 | { | ||
153 | mi_gvar_chg *aux; | ||
154 | |||
155 | while (p) | ||
156 | { | ||
157 | free(p->name); | ||
158 | free(p->new_type); | ||
159 | aux=p->next; | ||
160 | free(p); | ||
161 | p=aux; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | void mi_free_results_but(mi_results *r, mi_results *no) | ||
166 | { | ||
167 | mi_results *aux; | ||
168 | |||
169 | while (r) | ||
170 | { | ||
171 | if (r==no) | ||
172 | { | ||
173 | aux=r->next; | ||
174 | r->next=NULL; | ||
175 | r=aux; | ||
176 | } | ||
177 | else | ||
178 | { | ||
179 | free(r->var); | ||
180 | switch (r->type) | ||
181 | { | ||
182 | case t_const: | ||
183 | free(r->v.cstr); | ||
184 | break; | ||
185 | case t_tuple: | ||
186 | case t_list: | ||
187 | mi_free_results_but(r->v.rs,no); | ||
188 | break; | ||
189 | } | ||
190 | aux=r->next; | ||
191 | free(r); | ||
192 | r=aux; | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | void mi_free_results(mi_results *r) | ||
198 | { | ||
199 | mi_free_results_but(r,NULL); | ||
200 | } | ||
201 | |||
202 | void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r) | ||
203 | { | ||
204 | mi_output *aux; | ||
205 | |||
206 | while (r) | ||
207 | { | ||
208 | if (r==no) | ||
209 | { | ||
210 | aux=r->next; | ||
211 | r->next=NULL; | ||
212 | r=aux; | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | if (r->c) | ||
217 | mi_free_results_but(r->c,no_r); | ||
218 | aux=r->next; | ||
219 | free(r); | ||
220 | r=aux; | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | void mi_free_output(mi_output *r) | ||
226 | { | ||
227 | mi_free_output_but(r,NULL,NULL); | ||
228 | } | ||
229 | |||
230 | void mi_free_stop(mi_stop *s) | ||
231 | { | ||
232 | if (!s) | ||
233 | return; | ||
234 | mi_free_frames(s->frame); | ||
235 | mi_free_wp(s->wp); | ||
236 | free(s->wp_old); | ||
237 | free(s->wp_val); | ||
238 | free(s->gdb_result_var); | ||
239 | free(s->return_value); | ||
240 | free(s->signal_name); | ||
241 | free(s->signal_meaning); | ||
242 | free(s); | ||
243 | } | ||
244 | |||
245 | void mi_free_wp(mi_wp *wp) | ||
246 | { | ||
247 | mi_wp *aux; | ||
248 | while (wp) | ||
249 | { | ||
250 | free(wp->exp); | ||
251 | aux=wp->next; | ||
252 | free(wp); | ||
253 | wp=aux; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | void mi_free_asm_insns(mi_asm_insns *i) | ||
258 | { | ||
259 | mi_asm_insns *aux; | ||
260 | |||
261 | while (i) | ||
262 | { | ||
263 | free(i->file); | ||
264 | mi_free_asm_insn(i->ins); | ||
265 | aux=i->next; | ||
266 | free(i); | ||
267 | i=aux; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | void mi_free_asm_insn(mi_asm_insn *i) | ||
272 | { | ||
273 | mi_asm_insn *aux; | ||
274 | |||
275 | while (i) | ||
276 | { | ||
277 | free(i->func); | ||
278 | free(i->inst); | ||
279 | aux=i->next; | ||
280 | free(i); | ||
281 | i=aux; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /*void mi_free_charp_list(char **l) | ||
286 | { | ||
287 | char **c=l; | ||
288 | while (c) | ||
289 | { | ||
290 | free(*c); | ||
291 | c++; | ||
292 | } | ||
293 | free(l); | ||
294 | }*/ | ||
295 | |||
296 | void mi_free_chg_reg(mi_chg_reg *r) | ||
297 | { | ||
298 | mi_chg_reg *aux; | ||
299 | while (r) | ||
300 | { | ||
301 | free(r->val); | ||
302 | free(r->name); | ||
303 | aux=r->next; | ||
304 | free(r); | ||
305 | r=aux; | ||
306 | } | ||
307 | } | ||
308 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Breakpoint table commands. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Breakpoint Table Commands" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | |||
13 | -break-after Yes | ||
14 | -break-condition Yes | ||
15 | -break-delete Yes | ||
16 | -break-disable Yes | ||
17 | -break-enable Yes | ||
18 | -break-info N.A. (info break NUMBER) (*) | ||
19 | -break-insert Yes | ||
20 | -break-list No (*) | ||
21 | -break-watch Yes | ||
22 | @</pre> | ||
23 | |||
24 | (*) I think the program should keep track of the breakpoints, so it will | ||
25 | be implemented when I have more time.@p | ||
26 | |||
27 | ***************************************************************************/ | ||
28 | |||
29 | #include "mi_gdb.h" | ||
30 | |||
31 | /* Low level versions. */ | ||
32 | |||
33 | void mi_break_insert_fl(mi_h *h, const char *file, int line) | ||
34 | { | ||
35 | mi_send(h,"-break-insert %s:%d\n",file,line); | ||
36 | } | ||
37 | |||
38 | void mi_break_insert(mi_h *h, int temporary, int hard_assist, | ||
39 | const char *cond, int count, int thread, | ||
40 | const char *where) | ||
41 | { | ||
42 | char s_count[32]; | ||
43 | char s_thread[32]; | ||
44 | |||
45 | if (count>=0) | ||
46 | snprintf(s_count,32,"%d",count); | ||
47 | if (thread>=0) | ||
48 | snprintf(s_thread,32,"%d",thread); | ||
49 | if (cond) | ||
50 | // Conditions may contain spaces, in fact, if they don't gdb will add | ||
51 | // them after parsing. Enclosing the expression with "" solves the | ||
52 | // problem. | ||
53 | mi_send(h,"-break-insert %s %s -c \"%s\" %s %s %s %s %s\n", | ||
54 | temporary ? "-t" : "", | ||
55 | hard_assist ? "-h" : "", | ||
56 | cond, | ||
57 | count>=0 ? "-i" : "", count>=0 ? s_count : "", | ||
58 | thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", | ||
59 | where); | ||
60 | else | ||
61 | mi_send(h,"-break-insert %s %s %s %s %s %s %s\n", | ||
62 | temporary ? "-t" : "", | ||
63 | hard_assist ? "-h" : "", | ||
64 | count>=0 ? "-i" : "", count>=0 ? s_count : "", | ||
65 | thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", | ||
66 | where); | ||
67 | } | ||
68 | |||
69 | void mi_break_insert_flf(mi_h *h, const char *file, int line, int temporary, | ||
70 | int hard_assist, const char *cond, int count, | ||
71 | int thread) | ||
72 | { | ||
73 | char s_count[32]; | ||
74 | char s_thread[32]; | ||
75 | |||
76 | if (count>=0) | ||
77 | snprintf(s_count,32,"%d",count); | ||
78 | if (thread>=0) | ||
79 | snprintf(s_thread,32,"%d",thread); | ||
80 | mi_send(h,"-break-insert %s %s %s %s %s %s %s %s %s:%d\n", | ||
81 | temporary ? "-t" : "", | ||
82 | hard_assist ? "-h" : "", | ||
83 | cond ? "-c" : "", cond ? cond : "", | ||
84 | count>=0 ? "-i" : "", count>=0 ? s_count : "", | ||
85 | thread>=0 ? "-p" : "", thread>=0 ? s_thread : "", | ||
86 | file,line); | ||
87 | } | ||
88 | |||
89 | void mi_break_delete(mi_h *h, int number) | ||
90 | { | ||
91 | mi_send(h,"-break-delete %d\n",number); | ||
92 | } | ||
93 | |||
94 | void mi_break_after(mi_h *h, int number, int count) | ||
95 | { | ||
96 | mi_send(h,"-break-after %d %d\n",number,count); | ||
97 | } | ||
98 | |||
99 | void mi_break_condition(mi_h *h, int number, const char *condition) | ||
100 | { | ||
101 | mi_send(h,"-break-condition %d %s\n",number,condition); | ||
102 | } | ||
103 | |||
104 | void mi_break_enable(mi_h *h, int number) | ||
105 | { | ||
106 | mi_send(h,"-break-enable %d\n",number); | ||
107 | } | ||
108 | |||
109 | void mi_break_disable(mi_h *h, int number) | ||
110 | { | ||
111 | mi_send(h,"-break-disable %d\n",number); | ||
112 | } | ||
113 | |||
114 | void mi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp) | ||
115 | { | ||
116 | if (mode==wm_write) | ||
117 | mi_send(h,"-break-watch \"%s\"\n",exp); | ||
118 | else | ||
119 | mi_send(h,"-break-watch -%c \"%s\"\n",mode==wm_rw ? 'a' : 'r',exp); | ||
120 | } | ||
121 | |||
122 | /* High level versions. */ | ||
123 | |||
124 | /**[txh]******************************************************************** | ||
125 | |||
126 | Description: | ||
127 | Insert a breakpoint at file:line. | ||
128 | |||
129 | Command: -break-insert file:line | ||
130 | Return: A new mi_bkpt structure with info about the breakpoint. NULL on | ||
131 | error. | ||
132 | |||
133 | ***************************************************************************/ | ||
134 | |||
135 | mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line) | ||
136 | { | ||
137 | mi_break_insert_fl(h,file,line); | ||
138 | return mi_res_bkpt(h); | ||
139 | } | ||
140 | |||
141 | /**[txh]******************************************************************** | ||
142 | |||
143 | Description: | ||
144 | Insert a breakpoint, all available options. | ||
145 | |||
146 | Command: -break-insert | ||
147 | Return: A new mi_bkpt structure with info about the breakpoint. NULL on | ||
148 | error. | ||
149 | |||
150 | ***************************************************************************/ | ||
151 | |||
152 | mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, | ||
153 | const char *cond, int count, int thread, | ||
154 | const char *where) | ||
155 | { | ||
156 | mi_break_insert(h,temporary,hard_assist,cond,count,thread,where); | ||
157 | return mi_res_bkpt(h); | ||
158 | } | ||
159 | |||
160 | /**[txh]******************************************************************** | ||
161 | |||
162 | Description: | ||
163 | Insert a breakpoint, all available options. | ||
164 | |||
165 | Command: -break-insert [ops] file:line | ||
166 | Return: A new mi_bkpt structure with info about the breakpoint. NULL on | ||
167 | error. | ||
168 | |||
169 | ***************************************************************************/ | ||
170 | |||
171 | mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line, | ||
172 | int temporary, int hard_assist, | ||
173 | const char *cond, int count, int thread) | ||
174 | { | ||
175 | mi_break_insert_flf(h,file,line,temporary,hard_assist,cond,count,thread); | ||
176 | return mi_res_bkpt(h); | ||
177 | } | ||
178 | |||
179 | /**[txh]******************************************************************** | ||
180 | |||
181 | Description: | ||
182 | Remove a breakpoint. | ||
183 | |||
184 | Command: -break-delete | ||
185 | Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the | ||
186 | console. | ||
187 | |||
188 | ***************************************************************************/ | ||
189 | |||
190 | int gmi_break_delete(mi_h *h, int number) | ||
191 | { | ||
192 | mi_break_delete(h,number); | ||
193 | return mi_res_simple_done(h); | ||
194 | } | ||
195 | |||
196 | /**[txh]******************************************************************** | ||
197 | |||
198 | Description: | ||
199 | Modify the "ignore" count for a breakpoint. | ||
200 | |||
201 | Command: -break-after | ||
202 | Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the | ||
203 | console. | ||
204 | |||
205 | ***************************************************************************/ | ||
206 | |||
207 | int gmi_break_set_times(mi_h *h, int number, int count) | ||
208 | { | ||
209 | mi_break_after(h,number,count); | ||
210 | return mi_res_simple_done(h); | ||
211 | } | ||
212 | |||
213 | /**[txh]******************************************************************** | ||
214 | |||
215 | Description: | ||
216 | Associate a condition with the breakpoint. | ||
217 | |||
218 | Command: -break-condition | ||
219 | Return: !=0 OK | ||
220 | |||
221 | ***************************************************************************/ | ||
222 | |||
223 | int gmi_break_set_condition(mi_h *h, int number, const char *condition) | ||
224 | { | ||
225 | mi_break_condition(h,number,condition); | ||
226 | return mi_res_simple_done(h); | ||
227 | } | ||
228 | |||
229 | /**[txh]******************************************************************** | ||
230 | |||
231 | Description: | ||
232 | Enable or disable a breakpoint. | ||
233 | |||
234 | Command: -break-enable + -break-disable | ||
235 | Return: !=0 OK. Note that gdb always says OK, but errors can be sent to the | ||
236 | console. | ||
237 | |||
238 | ***************************************************************************/ | ||
239 | |||
240 | int gmi_break_state(mi_h *h, int number, int enable) | ||
241 | { | ||
242 | if (enable) | ||
243 | mi_break_enable(h,number); | ||
244 | else | ||
245 | mi_break_disable(h,number); | ||
246 | return mi_res_simple_done(h); | ||
247 | } | ||
248 | |||
249 | /**[txh]******************************************************************** | ||
250 | |||
251 | Description: | ||
252 | Set a watchpoint. It doesn't work for remote targets! | ||
253 | |||
254 | Command: -break-watch | ||
255 | Return: A new mi_wp structure with info about the watchpoint. NULL on | ||
256 | error. | ||
257 | |||
258 | ***************************************************************************/ | ||
259 | |||
260 | mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp) | ||
261 | { | ||
262 | mi_break_watch(h,mode,exp); | ||
263 | return mi_res_wp(h); | ||
264 | } | ||
265 | |||
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 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <string.h> | ||
3 | |||
4 | void crashFunction() | ||
5 | { | ||
6 | //char *stringCannotBeChanged = "String cannot be changed!"; | ||
7 | char *nullString = NULL; | ||
8 | |||
9 | printf("Now the program will crash! Take a cover! \n"); | ||
10 | //*stringCannotBeChanged = 'h'; | ||
11 | printf("Nonsense!\n"); | ||
12 | if (strcmp(nullString, "A string to compare with") == 0) { | ||
13 | printf("How come?! It had to be crashed!\n"); | ||
14 | } | ||
15 | } | ||
16 | |||
17 | int main(int argc, char *argv[]) | ||
18 | { | ||
19 | int i = 0; | ||
20 | printf("arguments: %d\n", argc); | ||
21 | for (i=0; i<argc; i++) | ||
22 | printf("%d: %s\n", i, argv[i]); | ||
23 | printf("Press ENTER\n"); | ||
24 | getchar(); | ||
25 | crashFunction(); | ||
26 | return 0; | ||
27 | } | ||
diff --git a/src/monkey/connect.c b/src/monkey/connect.c new file mode 100644 index 000000000..eaca967da --- /dev/null +++ b/src/monkey/connect.c | |||
@@ -0,0 +1,884 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2009 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Connect. | ||
7 | Comments: | ||
8 | This module handles the dialog with gdb, including starting and stopping | ||
9 | gdb.@p | ||
10 | |||
11 | GDB Bug workaround for "file -readnow": I tried to workaround a bug using | ||
12 | it but looks like this option also have bugs!!!! so I have to use the | ||
13 | command line option --readnow. | ||
14 | It also have a bug!!!! when the binary is changed and gdb must reload it | ||
15 | this option is ignored. So it looks like we have no solution but 3 gdb bugs | ||
16 | in a row. | ||
17 | |||
18 | ***************************************************************************/ | ||
19 | |||
20 | #define _GNU_SOURCE | ||
21 | #include <sys/types.h> | ||
22 | #include <unistd.h> | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <sys/wait.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <string.h> | ||
28 | #include <stdarg.h> | ||
29 | #include <limits.h> | ||
30 | #include <errno.h> | ||
31 | #include <signal.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <sys/time.h> | ||
34 | #include "mi_gdb.h" | ||
35 | |||
36 | #ifndef TEMP_FAILURE_RETRY | ||
37 | #define TEMP_FAILURE_RETRY(a) (a) | ||
38 | #endif | ||
39 | |||
40 | int mi_error=MI_OK; | ||
41 | char *mi_error_from_gdb=NULL; | ||
42 | static char *gdb_exe=NULL; | ||
43 | static char *xterm_exe=NULL; | ||
44 | static char *gdb_start=NULL; | ||
45 | static char *gdb_conn=NULL; | ||
46 | static char *main_func=NULL; | ||
47 | static char disable_psym_search_workaround=0; | ||
48 | |||
49 | mi_h *mi_alloc_h() | ||
50 | { | ||
51 | mi_h *h=(mi_h *)calloc(1,sizeof(mi_h)); | ||
52 | if (!h) | ||
53 | { | ||
54 | mi_error=MI_OUT_OF_MEMORY; | ||
55 | return NULL; | ||
56 | } | ||
57 | h->to_gdb[0]=h->to_gdb[1]=h->from_gdb[0]=h->from_gdb[1]=-1; | ||
58 | h->pid=-1; | ||
59 | return h; | ||
60 | } | ||
61 | |||
62 | int mi_check_running_pid(pid_t pid) | ||
63 | { | ||
64 | int status; | ||
65 | |||
66 | if (pid<=0) | ||
67 | return 0; | ||
68 | /* If waitpid returns the number of our child means it communicated | ||
69 | to as a termination status. */ | ||
70 | if (waitpid(pid,&status,WNOHANG)==pid) | ||
71 | { | ||
72 | pid=0; | ||
73 | return 0; | ||
74 | } | ||
75 | return 1; | ||
76 | } | ||
77 | |||
78 | int mi_check_running(mi_h *h) | ||
79 | { | ||
80 | return !h->died && mi_check_running_pid(h->pid); | ||
81 | } | ||
82 | |||
83 | void mi_kill_child(pid_t pid) | ||
84 | { | ||
85 | kill(pid,SIGTERM); | ||
86 | usleep(100000); | ||
87 | if (mi_check_running_pid(pid)) | ||
88 | { | ||
89 | int status; | ||
90 | kill(pid,SIGKILL); | ||
91 | waitpid(pid,&status,0); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | void mi_free_h(mi_h **handle) | ||
96 | { | ||
97 | mi_h *h=*handle; | ||
98 | if (h->to_gdb[0]>=0) | ||
99 | close(h->to_gdb[0]); | ||
100 | if (h->to) | ||
101 | fclose(h->to); | ||
102 | else if (h->to_gdb[1]>=0) | ||
103 | close(h->to_gdb[1]); | ||
104 | if (h->from) | ||
105 | fclose(h->from); | ||
106 | else if (h->from_gdb[0]>=0) | ||
107 | close(h->from_gdb[0]); | ||
108 | if (h->from_gdb[1]>=0) | ||
109 | close(h->from_gdb[1]); | ||
110 | if (mi_check_running(h)) | ||
111 | {/* GDB is running! */ | ||
112 | mi_kill_child(h->pid); | ||
113 | } | ||
114 | if (h->line) | ||
115 | free(h->line); | ||
116 | mi_free_output(h->po); | ||
117 | free(h->catched_console); | ||
118 | free(h); | ||
119 | *handle=NULL; | ||
120 | } | ||
121 | |||
122 | void mi_set_nonblk(int h) | ||
123 | { | ||
124 | int flf; | ||
125 | flf=fcntl(h,F_GETFL,0); | ||
126 | flf=flf | O_NONBLOCK; | ||
127 | fcntl(h,F_SETFL,flf); | ||
128 | } | ||
129 | |||
130 | int mi_getline(mi_h *h) | ||
131 | { | ||
132 | char c; | ||
133 | |||
134 | while (read(h->from_gdb[0],&c,1)==1) | ||
135 | { | ||
136 | if (h->lread>=h->llen) | ||
137 | { | ||
138 | h->llen=h->lread+128; | ||
139 | h->line=(char *)realloc(h->line,h->llen); | ||
140 | if (!h->line) | ||
141 | { | ||
142 | h->llen=0; | ||
143 | h->lread=0; | ||
144 | return -1; | ||
145 | } | ||
146 | } | ||
147 | if (c=='\n') | ||
148 | { | ||
149 | int ret=h->lread; | ||
150 | h->line[ret]=0; | ||
151 | h->lread=0; | ||
152 | return ret; | ||
153 | } | ||
154 | h->line[h->lread]=c; | ||
155 | h->lread++; | ||
156 | } | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | char *get_cstr(mi_output *o) | ||
161 | { | ||
162 | if (!o->c || o->c->type!=t_const) | ||
163 | return NULL; | ||
164 | return o->c->v.cstr; | ||
165 | } | ||
166 | |||
167 | int mi_get_response(mi_h *h) | ||
168 | { | ||
169 | int l=mi_getline(h); | ||
170 | if (!l) | ||
171 | return 0; | ||
172 | |||
173 | if (h->from_gdb_echo) | ||
174 | h->from_gdb_echo(h->line,h->from_gdb_echo_data); | ||
175 | if (strncmp(h->line,"(gdb)",5)==0) | ||
176 | {/* End of response. */ | ||
177 | return 1; | ||
178 | } | ||
179 | else | ||
180 | {/* Add to the response. */ | ||
181 | mi_output *o; | ||
182 | int add=1, is_exit=0; | ||
183 | o=mi_parse_gdb_output(h->line); | ||
184 | |||
185 | if (!o) | ||
186 | return 0; | ||
187 | /* Tunneled streams callbacks. */ | ||
188 | if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_STREAM) | ||
189 | { | ||
190 | char *aux; | ||
191 | add=0; | ||
192 | switch (o->sstype) | ||
193 | { | ||
194 | case MI_SST_CONSOLE: | ||
195 | aux=get_cstr(o); | ||
196 | if (h->console) | ||
197 | h->console(aux,h->console_data); | ||
198 | if (h->catch_console && aux) | ||
199 | { | ||
200 | h->catch_console--; | ||
201 | if (!h->catch_console) | ||
202 | { | ||
203 | free(h->catched_console); | ||
204 | h->catched_console=strdup(aux); | ||
205 | } | ||
206 | } | ||
207 | break; | ||
208 | case MI_SST_TARGET: | ||
209 | /* This one seems to be useless. */ | ||
210 | if (h->target) | ||
211 | h->target(get_cstr(o),h->target_data); | ||
212 | break; | ||
213 | case MI_SST_LOG: | ||
214 | if (h->log) | ||
215 | h->log(get_cstr(o),h->log_data); | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | else if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_ASYNC) | ||
220 | { | ||
221 | if (h->async) | ||
222 | h->async(o,h->async_data); | ||
223 | } | ||
224 | else if (o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_ERROR) | ||
225 | {/* Error from gdb, record it. */ | ||
226 | mi_error=MI_FROM_GDB; | ||
227 | free(mi_error_from_gdb); | ||
228 | mi_error_from_gdb=NULL; | ||
229 | if (o->c && strcmp(o->c->var,"msg")==0 && o->c->type==t_const) | ||
230 | mi_error_from_gdb=strdup(o->c->v.cstr); | ||
231 | } | ||
232 | is_exit=(o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_EXIT); | ||
233 | /* Add to the list of responses. */ | ||
234 | if (add) | ||
235 | { | ||
236 | if (h->last) | ||
237 | h->last->next=o; | ||
238 | else | ||
239 | h->po=o; | ||
240 | h->last=o; | ||
241 | } | ||
242 | else | ||
243 | mi_free_output(o); | ||
244 | /* Exit RR means gdb exited, we won't get a new prompt ;-) */ | ||
245 | if (is_exit) | ||
246 | return 1; | ||
247 | } | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | mi_output *mi_retire_response(mi_h *h) | ||
253 | { | ||
254 | mi_output *ret=h->po; | ||
255 | h->po=h->last=NULL; | ||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | mi_output *mi_get_response_blk(mi_h *h) | ||
260 | { | ||
261 | int r; | ||
262 | /* Sometimes gdb dies. */ | ||
263 | if (!mi_check_running(h)) | ||
264 | { | ||
265 | h->died=1; | ||
266 | mi_error=MI_GDB_DIED; | ||
267 | return NULL; | ||
268 | } | ||
269 | do | ||
270 | { | ||
271 | if (1) | ||
272 | { | ||
273 | /* | ||
274 | That's a must. If we just keep trying to read and failing things | ||
275 | become really sloooowwww. Instead we try and if it fails we wait | ||
276 | until something is available. | ||
277 | TODO: Implement something with the time out, a callback to ask the | ||
278 | application is we have to wait or not could be a good thing. | ||
279 | */ | ||
280 | fd_set set; | ||
281 | struct timeval timeout; | ||
282 | int ret; | ||
283 | |||
284 | r=mi_get_response(h); | ||
285 | if (r) | ||
286 | return mi_retire_response(h); | ||
287 | |||
288 | FD_ZERO(&set); | ||
289 | FD_SET(h->from_gdb[0],&set); | ||
290 | timeout.tv_sec=h->time_out; | ||
291 | timeout.tv_usec=0; | ||
292 | ret=TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,&timeout)); | ||
293 | if (!ret) | ||
294 | { | ||
295 | if (!mi_check_running(h)) | ||
296 | { | ||
297 | h->died=1; | ||
298 | mi_error=MI_GDB_DIED; | ||
299 | return NULL; | ||
300 | } | ||
301 | if (h->time_out_cb) | ||
302 | ret=h->time_out_cb(h->time_out_cb_data); | ||
303 | if (!ret) | ||
304 | { | ||
305 | mi_error=MI_GDB_TIME_OUT; | ||
306 | return NULL; | ||
307 | } | ||
308 | } | ||
309 | } | ||
310 | else | ||
311 | { | ||
312 | r=mi_get_response(h); | ||
313 | if (r) | ||
314 | return mi_retire_response(h); | ||
315 | else | ||
316 | usleep(100); | ||
317 | } | ||
318 | } | ||
319 | while (!r); | ||
320 | |||
321 | return NULL; | ||
322 | } | ||
323 | |||
324 | void mi_send_commands(mi_h *h, const char *file) | ||
325 | { | ||
326 | FILE *f; | ||
327 | char b[PATH_MAX]; | ||
328 | |||
329 | //printf("File: %s\n",file); | ||
330 | if (!file) | ||
331 | return; | ||
332 | f=fopen(file,"rt"); | ||
333 | if (!f) | ||
334 | return; | ||
335 | while (!feof(f)) | ||
336 | { | ||
337 | if (fgets(b,PATH_MAX,f)) | ||
338 | { | ||
339 | //printf("Send: %s\n",b); | ||
340 | mi_send(h,b); | ||
341 | mi_res_simple_done(h); | ||
342 | } | ||
343 | } | ||
344 | fclose(f); | ||
345 | } | ||
346 | |||
347 | void mi_send_target_commands(mi_h *h) | ||
348 | { | ||
349 | mi_send_commands(h,gdb_conn); | ||
350 | } | ||
351 | |||
352 | /**[txh]******************************************************************** | ||
353 | |||
354 | Description: | ||
355 | Connect to a local copy of gdb. Note that the mi_h structure is something | ||
356 | similar to a "FILE *" for stdio. | ||
357 | |||
358 | Return: A new mi_h structure or NULL on error. | ||
359 | |||
360 | ***************************************************************************/ | ||
361 | |||
362 | mi_h *mi_connect_local() | ||
363 | { | ||
364 | mi_h *h; | ||
365 | const char *gdb=mi_get_gdb_exe(); | ||
366 | |||
367 | /* Start without error. */ | ||
368 | mi_error=MI_OK; | ||
369 | /* Verify we have a GDB binary. */ | ||
370 | if (access(gdb,X_OK)) | ||
371 | { | ||
372 | mi_error=MI_MISSING_GDB; | ||
373 | return NULL; | ||
374 | } | ||
375 | /* Alloc the handle structure. */ | ||
376 | h=mi_alloc_h(); | ||
377 | if (!h) | ||
378 | return h; | ||
379 | h->time_out=MI_DEFAULT_TIME_OUT; | ||
380 | /* Create the pipes to connect with the child. */ | ||
381 | if (pipe(h->to_gdb) || pipe(h->from_gdb)) | ||
382 | { | ||
383 | mi_error=MI_PIPE_CREATE; | ||
384 | mi_free_h(&h); | ||
385 | return NULL; | ||
386 | } | ||
387 | mi_set_nonblk(h->to_gdb[1]); | ||
388 | mi_set_nonblk(h->from_gdb[0]); | ||
389 | /* Associate streams to the file handles. */ | ||
390 | h->to=fdopen(h->to_gdb[1],"w"); | ||
391 | h->from=fdopen(h->from_gdb[0],"r"); | ||
392 | if (!h->to || !h->from) | ||
393 | { | ||
394 | mi_error=MI_PIPE_CREATE; | ||
395 | mi_free_h(&h); | ||
396 | return NULL; | ||
397 | } | ||
398 | /* Create the child. */ | ||
399 | h->pid=fork(); | ||
400 | if (h->pid==0) | ||
401 | {/* We are the child. */ | ||
402 | char *argv[5]; | ||
403 | /* Connect stdin/out to the pipes. */ | ||
404 | dup2(h->to_gdb[0],STDIN_FILENO); | ||
405 | dup2(h->from_gdb[1],STDOUT_FILENO); | ||
406 | /* Pass the control to gdb. */ | ||
407 | argv[0]=(char *)gdb; /* Is that OK? */ | ||
408 | argv[1]="--interpreter=mi"; | ||
409 | argv[2]="--quiet"; | ||
410 | argv[3]=disable_psym_search_workaround ? 0 : "--readnow"; | ||
411 | argv[4]=0; | ||
412 | execvp(argv[0],argv); | ||
413 | /* We get here only if exec failed. */ | ||
414 | _exit(127); | ||
415 | } | ||
416 | /* We are the parent. */ | ||
417 | if (h->pid==-1) | ||
418 | {/* Fork failed. */ | ||
419 | mi_error=MI_FORK; | ||
420 | mi_free_h(&h); | ||
421 | return NULL; | ||
422 | } | ||
423 | if (!mi_check_running(h)) | ||
424 | { | ||
425 | mi_error=MI_DEBUGGER_RUN; | ||
426 | mi_free_h(&h); | ||
427 | return NULL; | ||
428 | } | ||
429 | /* Wait for the prompt. */ | ||
430 | mi_get_response_blk(h); | ||
431 | /* Send the start-up commands */ | ||
432 | mi_send_commands(h,gdb_start); | ||
433 | |||
434 | return h; | ||
435 | } | ||
436 | |||
437 | /**[txh]******************************************************************** | ||
438 | |||
439 | Description: | ||
440 | Close connection. You should ask gdb to quit first @x{gmi_gdb_exit}. | ||
441 | |||
442 | ***************************************************************************/ | ||
443 | |||
444 | void mi_disconnect(mi_h *h) | ||
445 | { | ||
446 | mi_free_h(&h); | ||
447 | free(mi_error_from_gdb); | ||
448 | mi_error_from_gdb=NULL; | ||
449 | } | ||
450 | |||
451 | void mi_set_console_cb(mi_h *h, stream_cb cb, void *data) | ||
452 | { | ||
453 | h->console=cb; | ||
454 | h->console_data=data; | ||
455 | } | ||
456 | |||
457 | void mi_set_target_cb(mi_h *h, stream_cb cb, void *data) | ||
458 | { | ||
459 | h->target=cb; | ||
460 | h->target_data=data; | ||
461 | } | ||
462 | |||
463 | void mi_set_log_cb(mi_h *h, stream_cb cb, void *data) | ||
464 | { | ||
465 | h->log=cb; | ||
466 | h->log_data=data; | ||
467 | } | ||
468 | |||
469 | stream_cb mi_get_console_cb(mi_h *h, void **data) | ||
470 | { | ||
471 | if (data) | ||
472 | *data=h->console_data; | ||
473 | return h->console; | ||
474 | } | ||
475 | |||
476 | stream_cb mi_get_target_cb(mi_h *h, void **data) | ||
477 | { | ||
478 | if (data) | ||
479 | *data=h->target_data; | ||
480 | return h->target; | ||
481 | } | ||
482 | |||
483 | stream_cb mi_get_log_cb(mi_h *h, void **data) | ||
484 | { | ||
485 | if (data) | ||
486 | *data=h->log_data; | ||
487 | return h->log; | ||
488 | } | ||
489 | |||
490 | void mi_set_async_cb(mi_h *h, async_cb cb, void *data) | ||
491 | { | ||
492 | h->async=cb; | ||
493 | h->async_data=data; | ||
494 | } | ||
495 | |||
496 | async_cb mi_get_async_cb(mi_h *h, void **data) | ||
497 | { | ||
498 | if (data) | ||
499 | *data=h->async_data; | ||
500 | return h->async; | ||
501 | } | ||
502 | |||
503 | void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
504 | { | ||
505 | h->to_gdb_echo=cb; | ||
506 | h->to_gdb_echo_data=data; | ||
507 | } | ||
508 | |||
509 | void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
510 | { | ||
511 | h->from_gdb_echo=cb; | ||
512 | h->from_gdb_echo_data=data; | ||
513 | } | ||
514 | |||
515 | stream_cb mi_get_to_gdb_cb(mi_h *h, void **data) | ||
516 | { | ||
517 | if (data) | ||
518 | *data=h->to_gdb_echo_data; | ||
519 | return h->to_gdb_echo; | ||
520 | } | ||
521 | |||
522 | stream_cb mi_get_from_gdb_cb(mi_h *h, void **data) | ||
523 | { | ||
524 | if (data) | ||
525 | *data=h->from_gdb_echo_data; | ||
526 | return h->from_gdb_echo; | ||
527 | } | ||
528 | |||
529 | void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data) | ||
530 | { | ||
531 | h->time_out_cb=cb; | ||
532 | h->time_out_cb_data=data; | ||
533 | } | ||
534 | |||
535 | tm_cb mi_get_time_out_cb(mi_h *h, void **data) | ||
536 | { | ||
537 | if (data) | ||
538 | *data=h->time_out_cb_data; | ||
539 | return h->time_out_cb; | ||
540 | } | ||
541 | |||
542 | void mi_set_time_out(mi_h *h, int to) | ||
543 | { | ||
544 | h->time_out=to; | ||
545 | } | ||
546 | |||
547 | int mi_get_time_out(mi_h *h) | ||
548 | { | ||
549 | return h->time_out; | ||
550 | } | ||
551 | |||
552 | int mi_send(mi_h *h, const char *format, ...) | ||
553 | { | ||
554 | int ret; | ||
555 | char *str; | ||
556 | va_list argptr; | ||
557 | |||
558 | if (h->died) | ||
559 | return 0; | ||
560 | |||
561 | va_start(argptr,format); | ||
562 | ret=vasprintf(&str,format,argptr); | ||
563 | va_end(argptr); | ||
564 | fputs(str,h->to); | ||
565 | fflush(h->to); | ||
566 | if (h->to_gdb_echo) | ||
567 | h->to_gdb_echo(str,h->to_gdb_echo_data); | ||
568 | free(str); | ||
569 | |||
570 | return ret; | ||
571 | } | ||
572 | |||
573 | void mi_clean_up_globals() | ||
574 | { | ||
575 | free(gdb_exe); | ||
576 | gdb_exe=NULL; | ||
577 | free(xterm_exe); | ||
578 | xterm_exe=NULL; | ||
579 | free(gdb_start); | ||
580 | gdb_start=NULL; | ||
581 | free(gdb_conn); | ||
582 | gdb_conn=NULL; | ||
583 | free(main_func); | ||
584 | main_func=NULL; | ||
585 | } | ||
586 | |||
587 | void mi_register_exit() | ||
588 | { | ||
589 | static int registered=0; | ||
590 | if (!registered) | ||
591 | { | ||
592 | registered=1; | ||
593 | atexit(mi_clean_up_globals); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | void mi_set_gdb_exe(const char *name) | ||
598 | { | ||
599 | free(gdb_exe); | ||
600 | gdb_exe=name ? strdup(name) : NULL; | ||
601 | mi_register_exit(); | ||
602 | } | ||
603 | |||
604 | void mi_set_gdb_start(const char *name) | ||
605 | { | ||
606 | free(gdb_start); | ||
607 | gdb_start=name ? strdup(name) : NULL; | ||
608 | mi_register_exit(); | ||
609 | } | ||
610 | |||
611 | void mi_set_gdb_conn(const char *name) | ||
612 | { | ||
613 | free(gdb_conn); | ||
614 | gdb_conn=name ? strdup(name) : NULL; | ||
615 | mi_register_exit(); | ||
616 | } | ||
617 | |||
618 | static | ||
619 | char *mi_search_in_path(const char *file) | ||
620 | { | ||
621 | char *path, *pt, *r; | ||
622 | char test[PATH_MAX]; | ||
623 | struct stat st; | ||
624 | |||
625 | path=getenv("PATH"); | ||
626 | if (!path) | ||
627 | return NULL; | ||
628 | pt=strdup(path); | ||
629 | r=strtok(pt,":"); | ||
630 | while (r) | ||
631 | { | ||
632 | strcpy(test,r); | ||
633 | strcat(test,"/"); | ||
634 | strcat(test,file); | ||
635 | if (stat(test,&st)==0 && S_ISREG(st.st_mode)) | ||
636 | { | ||
637 | free(pt); | ||
638 | return strdup(test); | ||
639 | } | ||
640 | r=strtok(NULL,":"); | ||
641 | } | ||
642 | free(pt); | ||
643 | return NULL; | ||
644 | } | ||
645 | |||
646 | const char *mi_get_gdb_exe() | ||
647 | { | ||
648 | if (!gdb_exe) | ||
649 | {/* Look for gdb in path */ | ||
650 | gdb_exe=mi_search_in_path("gdb"); | ||
651 | if (!gdb_exe) | ||
652 | return "/usr/bin/gdb"; | ||
653 | } | ||
654 | return gdb_exe; | ||
655 | } | ||
656 | |||
657 | const char *mi_get_gdb_start() | ||
658 | { | ||
659 | return gdb_start; | ||
660 | } | ||
661 | |||
662 | const char *mi_get_gdb_conn() | ||
663 | { | ||
664 | return gdb_conn; | ||
665 | } | ||
666 | |||
667 | void mi_set_xterm_exe(const char *name) | ||
668 | { | ||
669 | free(xterm_exe); | ||
670 | xterm_exe=name ? strdup(name) : NULL; | ||
671 | mi_register_exit(); | ||
672 | } | ||
673 | |||
674 | const char *mi_get_xterm_exe() | ||
675 | { | ||
676 | if (!xterm_exe) | ||
677 | {/* Look for xterm in path */ | ||
678 | xterm_exe=mi_search_in_path("xterm"); | ||
679 | if (!xterm_exe) | ||
680 | return "/usr/bin/X11/xterm"; | ||
681 | } | ||
682 | return xterm_exe; | ||
683 | } | ||
684 | |||
685 | void mi_set_main_func(const char *name) | ||
686 | { | ||
687 | free(main_func); | ||
688 | main_func=name ? strdup(name) : NULL; | ||
689 | mi_register_exit(); | ||
690 | } | ||
691 | |||
692 | const char *mi_get_main_func() | ||
693 | { | ||
694 | if (main_func) | ||
695 | return main_func; | ||
696 | return "main"; | ||
697 | } | ||
698 | |||
699 | /**[txh]******************************************************************** | ||
700 | |||
701 | Description: | ||
702 | Opens a new xterm to be used by the child process to debug. | ||
703 | |||
704 | Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to | ||
705 | release it. | ||
706 | |||
707 | ***************************************************************************/ | ||
708 | |||
709 | mi_aux_term *gmi_start_xterm() | ||
710 | { | ||
711 | char nsh[14]="/tmp/shXXXXXX"; | ||
712 | char ntt[14]="/tmp/ttXXXXXX"; | ||
713 | const char *xterm; | ||
714 | struct stat st; | ||
715 | int hsh, htt=-1; | ||
716 | mi_aux_term *res=NULL; | ||
717 | FILE *f; | ||
718 | pid_t pid; | ||
719 | char buf[PATH_MAX]; | ||
720 | |||
721 | /* Verify we have an X terminal. */ | ||
722 | xterm=mi_get_xterm_exe(); | ||
723 | if (access(xterm,X_OK)) | ||
724 | { | ||
725 | mi_error=MI_MISSING_XTERM; | ||
726 | return NULL; | ||
727 | } | ||
728 | |||
729 | /* Create 2 temporals. */ | ||
730 | hsh=mkstemp(nsh); | ||
731 | if (hsh==-1) | ||
732 | { | ||
733 | mi_error=MI_CREATE_TEMPORAL; | ||
734 | return NULL; | ||
735 | } | ||
736 | htt=mkstemp(ntt); | ||
737 | if (htt==-1) | ||
738 | { | ||
739 | close(hsh); | ||
740 | unlink(nsh); | ||
741 | mi_error=MI_CREATE_TEMPORAL; | ||
742 | return NULL; | ||
743 | } | ||
744 | close(htt); | ||
745 | /* Create the script. */ | ||
746 | f=fdopen(hsh,"w"); | ||
747 | if (!f) | ||
748 | { | ||
749 | close(hsh); | ||
750 | unlink(nsh); | ||
751 | unlink(ntt); | ||
752 | mi_error=MI_CREATE_TEMPORAL; | ||
753 | return NULL; | ||
754 | } | ||
755 | fprintf(f,"#!/bin/sh\n"); | ||
756 | fprintf(f,"tty > %s\n",ntt); | ||
757 | fprintf(f,"rm %s\n",nsh); | ||
758 | fprintf(f,"sleep 365d\n"); | ||
759 | fclose(f); | ||
760 | /* Spawn xterm. */ | ||
761 | /* Create the child. */ | ||
762 | pid=fork(); | ||
763 | if (pid==0) | ||
764 | {/* We are the child. */ | ||
765 | char *argv[5]; | ||
766 | /* Pass the control to gdb. */ | ||
767 | argv[0]=(char *)mi_get_xterm_exe(); /* Is that ok? */ | ||
768 | argv[1]="-e"; | ||
769 | argv[2]="/bin/sh"; | ||
770 | argv[3]=nsh; | ||
771 | argv[4]=0; | ||
772 | execvp(argv[0],argv); | ||
773 | /* We get here only if exec failed. */ | ||
774 | unlink(nsh); | ||
775 | unlink(ntt); | ||
776 | _exit(127); | ||
777 | } | ||
778 | /* We are the parent. */ | ||
779 | if (pid==-1) | ||
780 | {/* Fork failed. */ | ||
781 | unlink(nsh); | ||
782 | unlink(ntt); | ||
783 | mi_error=MI_FORK; | ||
784 | return NULL; | ||
785 | } | ||
786 | /* Wait until the shell is deleted. */ | ||
787 | while (stat(nsh,&st)==0) | ||
788 | usleep(1000); | ||
789 | /* Try to read the tty name. */ | ||
790 | f=fopen(ntt,"rt"); | ||
791 | if (f) | ||
792 | { | ||
793 | if (fgets(buf,PATH_MAX,f)) | ||
794 | { | ||
795 | char *s; /* Strip the \n. */ | ||
796 | for (s=buf; *s && *s!='\n'; s++); | ||
797 | *s=0; | ||
798 | res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); | ||
799 | if (res) | ||
800 | { | ||
801 | res->pid=pid; | ||
802 | res->tty=strdup(buf); | ||
803 | } | ||
804 | } | ||
805 | fclose(f); | ||
806 | } | ||
807 | unlink(ntt); | ||
808 | return res; | ||
809 | } | ||
810 | |||
811 | void mi_free_aux_term(mi_aux_term *t) | ||
812 | { | ||
813 | if (!t) | ||
814 | return; | ||
815 | free(t->tty); | ||
816 | free(t); | ||
817 | } | ||
818 | |||
819 | /**[txh]******************************************************************** | ||
820 | |||
821 | Description: | ||
822 | Closes the auxiliar terminal and releases the allocated memory. | ||
823 | |||
824 | ***************************************************************************/ | ||
825 | |||
826 | void gmi_end_aux_term(mi_aux_term *t) | ||
827 | { | ||
828 | if (!t) | ||
829 | return; | ||
830 | if (t->pid!=-1 && mi_check_running_pid(t->pid)) | ||
831 | mi_kill_child(t->pid); | ||
832 | mi_free_aux_term(t); | ||
833 | } | ||
834 | |||
835 | /**[txh]******************************************************************** | ||
836 | |||
837 | Description: | ||
838 | Forces the MI version. Currently the library can't detect it so you must | ||
839 | force it manually. GDB 5.x implemented MI v1 and 6.x v2. | ||
840 | |||
841 | ***************************************************************************/ | ||
842 | |||
843 | void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, | ||
844 | unsigned vMinor) | ||
845 | { | ||
846 | h->version=MI_VERSION2U(vMajor,vMiddle,vMinor); | ||
847 | } | ||
848 | |||
849 | /**[txh]******************************************************************** | ||
850 | |||
851 | Description: | ||
852 | Dis/Enables the @var{wa} workaround for a bug in gdb. | ||
853 | |||
854 | ***************************************************************************/ | ||
855 | |||
856 | void mi_set_workaround(unsigned wa, int enable) | ||
857 | { | ||
858 | switch (wa) | ||
859 | { | ||
860 | case MI_PSYM_SEARCH: | ||
861 | disable_psym_search_workaround=enable ? 0 : 1; | ||
862 | break; | ||
863 | } | ||
864 | } | ||
865 | |||
866 | /**[txh]******************************************************************** | ||
867 | |||
868 | Description: | ||
869 | Finds if the @var{wa} workaround for a bug in gdb is enabled. | ||
870 | |||
871 | Return: !=0 if enabled. | ||
872 | |||
873 | ***************************************************************************/ | ||
874 | |||
875 | int mi_get_workaround(unsigned wa) | ||
876 | { | ||
877 | switch (wa) | ||
878 | { | ||
879 | case MI_PSYM_SEARCH: | ||
880 | return disable_psym_search_workaround==0; | ||
881 | } | ||
882 | return 0; | ||
883 | } | ||
884 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2007 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: C++ Interface. | ||
7 | Comments: | ||
8 | Implements a very simple (naive ;-) C++ wrapper.@p | ||
9 | |||
10 | ***************************************************************************/ | ||
11 | |||
12 | #include <string.h> | ||
13 | #include <limits.h> | ||
14 | #include "mi_gdb.h" | ||
15 | |||
16 | /**[txh]******************************************************************** | ||
17 | |||
18 | Description: | ||
19 | Initializes a debugger object. It starts in the "disconnected" state. | ||
20 | Use @x{::Connect} after it. | ||
21 | |||
22 | ***************************************************************************/ | ||
23 | |||
24 | MIDebugger::MIDebugger() | ||
25 | { | ||
26 | state=disconnected; | ||
27 | h=NULL; | ||
28 | aux_tty=NULL; | ||
29 | waitingTempBkpt=0; | ||
30 | targetEndian=enUnknown; | ||
31 | targetArch=arUnknown; | ||
32 | } | ||
33 | |||
34 | /**[txh]******************************************************************** | ||
35 | |||
36 | Description: | ||
37 | This is the destructor for the class. It tries to change the state to | ||
38 | "disconnected" doing the needed actions. | ||
39 | |||
40 | ***************************************************************************/ | ||
41 | |||
42 | MIDebugger::~MIDebugger() | ||
43 | { | ||
44 | if (state==running) | ||
45 | { | ||
46 | Stop(); | ||
47 | mi_stop *rs; | ||
48 | // TODO: Some kind of time-out | ||
49 | while (!Poll(rs)); | ||
50 | mi_free_stop(rs); | ||
51 | state=stopped; | ||
52 | } | ||
53 | if (state==stopped) | ||
54 | { | ||
55 | Kill(); | ||
56 | state=target_specified; | ||
57 | } | ||
58 | if (state==target_specified) | ||
59 | { | ||
60 | TargetUnselect(); | ||
61 | state=connected; | ||
62 | } | ||
63 | if (state==connected) | ||
64 | Disconnect(); | ||
65 | // Here state==disconnected | ||
66 | } | ||
67 | |||
68 | /**[txh]******************************************************************** | ||
69 | |||
70 | Description: | ||
71 | Connects to gdb. Currently only local connections are supported, that's | ||
72 | a gdb limitation. Call it when in "unconnected" state, on success it will | ||
73 | change to the "connected" state. After it you should call one of the | ||
74 | SelectTarget members. @x{::SelectTargetX11}, @x{::SelectTargetLinux} or | ||
75 | @x{::SelectTargetRemote}. To finish the connection use @x{::Disconnect}. | ||
76 | |||
77 | Return: !=0 OK. | ||
78 | |||
79 | ***************************************************************************/ | ||
80 | |||
81 | int MIDebugger::Connect(bool ) | ||
82 | { | ||
83 | if (state==disconnected) | ||
84 | { | ||
85 | h=mi_connect_local(); | ||
86 | if (h!=NULL) | ||
87 | { | ||
88 | state=connected; | ||
89 | return 1; | ||
90 | } | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /**[txh]******************************************************************** | ||
96 | |||
97 | Description: | ||
98 | Finishes the connection to gdb. Call when in "connected" state, on success | ||
99 | it will change to "disconnected" state. This function first tries to exit | ||
100 | from gdb and then close the connection. But if gdb fails to exit it will be | ||
101 | killed. | ||
102 | |||
103 | Return: !=0 OK | ||
104 | |||
105 | ***************************************************************************/ | ||
106 | |||
107 | int MIDebugger::Disconnect() | ||
108 | { | ||
109 | if (state==connected) | ||
110 | { | ||
111 | gmi_gdb_exit(h); | ||
112 | mi_disconnect(h); | ||
113 | state=disconnected; | ||
114 | return 1; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /**[txh]******************************************************************** | ||
120 | |||
121 | Description: | ||
122 | Protected member that implements @x{::SelectTargetX11} and | ||
123 | @x{::SelectTargetLinux}. | ||
124 | |||
125 | Return: !=0 OK. | ||
126 | |||
127 | ***************************************************************************/ | ||
128 | |||
129 | int MIDebugger::SelectTargetTTY(const char *exec, const char *args, | ||
130 | const char *auxtty, dMode m) | ||
131 | { | ||
132 | if (state!=connected) | ||
133 | return 0; | ||
134 | |||
135 | targetEndian=enUnknown; | ||
136 | targetArch=arUnknown; | ||
137 | mode=m; | ||
138 | if (!gmi_set_exec(h,exec,args)) | ||
139 | return 0; | ||
140 | |||
141 | const char *tty_name; | ||
142 | #ifndef __CYGWIN__ | ||
143 | if (!auxtty) | ||
144 | { | ||
145 | aux_tty=m==dmLinux ? gmi_look_for_free_vt() : gmi_start_xterm(); | ||
146 | if (!aux_tty) | ||
147 | return 0; | ||
148 | tty_name=aux_tty->tty; | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | tty_name=auxtty; | ||
153 | } | ||
154 | if (!gmi_target_terminal(h,tty_name)) | ||
155 | return 0; | ||
156 | #else | ||
157 | tty_name=NULL; | ||
158 | if (!gmi_gdb_set(h,"new-console","on")) | ||
159 | return 0; | ||
160 | #endif | ||
161 | |||
162 | state=target_specified; | ||
163 | preRun=false; | ||
164 | return 1; | ||
165 | } | ||
166 | |||
167 | /**[txh]******************************************************************** | ||
168 | |||
169 | Description: | ||
170 | Starts a debug session for X11. It opens an xterm console for the program | ||
171 | to debug and tells gdb which executable to debug and the command line | ||
172 | options to pass. You can specify an already existing tty console to be used. | ||
173 | Can be called when the state is "connected". On success will change to the | ||
174 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
175 | to define breakpoints and similar stuff. To finish it use | ||
176 | @x{::TargetUnselect}. | ||
177 | |||
178 | Return: !=0 OK. | ||
179 | |||
180 | ***************************************************************************/ | ||
181 | |||
182 | int MIDebugger::SelectTargetX11(const char *exec, const char *args, | ||
183 | const char *auxtty) | ||
184 | { | ||
185 | return SelectTargetTTY(exec,args,auxtty,dmX11); | ||
186 | } | ||
187 | |||
188 | |||
189 | /**[txh]******************************************************************** | ||
190 | |||
191 | Description: | ||
192 | Starts a debug session for Linux console. It selects an empty VT for the | ||
193 | program to debug and tells gdb which executable to debug and the command line | ||
194 | options to pass. You can specify an already existing tty console to be used. | ||
195 | Can be called when the state is "connected". On success will change to the | ||
196 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
197 | to define breakpoints and similar stuff. To finish it use | ||
198 | @x{::TargetUnselect}. | ||
199 | |||
200 | Return: !=0 OK. | ||
201 | |||
202 | ***************************************************************************/ | ||
203 | |||
204 | int MIDebugger::SelectTargetLinux(const char *exec, const char *args, | ||
205 | const char *auxtty) | ||
206 | { | ||
207 | return SelectTargetTTY(exec,args,auxtty,dmLinux); | ||
208 | } | ||
209 | |||
210 | /**[txh]******************************************************************** | ||
211 | |||
212 | Description: | ||
213 | Starts a remote session. The other end should be running gdbserver. You | ||
214 | must specify a local copy of the program to debug with debug info. The remote | ||
215 | copy can be stripped. The @var{rtype} and @var{rparams} selects the protocol | ||
216 | and the remote machine. Read gdb docs to know more about the available | ||
217 | options. If @var{rtype} is omitted "extended-remote" protocol is used. | ||
218 | Can be called when the state is "connected". On success will change to the | ||
219 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
220 | to define breakpoints and similar stuff. To finish it use | ||
221 | @x{::TargetUnselect}. Note that when gdb uses remote debugging the remote | ||
222 | program starts running. The @x{::Run} member knows about it. | ||
223 | |||
224 | Return: !=0 OK. | ||
225 | Example: | ||
226 | o->SelectTargetRemote("./exec_file","192.168.1.65:5000"); | ||
227 | |||
228 | ***************************************************************************/ | ||
229 | |||
230 | int MIDebugger::SelectTargetRemote(const char *exec, const char *rparams, | ||
231 | const char *rtype, bool download) | ||
232 | { | ||
233 | if (state!=connected) | ||
234 | return 0; | ||
235 | |||
236 | mode=dmRemote; | ||
237 | preRun=true; | ||
238 | targetEndian=enUnknown; | ||
239 | targetArch=arUnknown; | ||
240 | if (rtype==NULL) | ||
241 | rtype="extended-remote"; | ||
242 | |||
243 | /* Tell gdb to load symbols from the local copy. */ | ||
244 | int res=download ? gmi_set_exec(h,exec,NULL) : gmi_file_symbol_file(h,exec); | ||
245 | if (!res) | ||
246 | return 0; | ||
247 | /* Select the target */ | ||
248 | if (!gmi_target_select(h,rtype,rparams)) | ||
249 | return 0; | ||
250 | /* Download the binary */ | ||
251 | if (download) | ||
252 | { | ||
253 | if (!gmi_target_download(h)) | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | state=target_specified; | ||
258 | return 1; | ||
259 | } | ||
260 | |||
261 | /**[txh]******************************************************************** | ||
262 | |||
263 | Description: | ||
264 | Starts a local session using an already running process. | ||
265 | |||
266 | Return: !=0 OK. | ||
267 | |||
268 | ***************************************************************************/ | ||
269 | |||
270 | mi_frames *MIDebugger::SelectTargetPID(const char *exec, int pid) | ||
271 | { | ||
272 | if (state!=connected) | ||
273 | return NULL; | ||
274 | |||
275 | mode=dmPID; | ||
276 | preRun=false; | ||
277 | targetEndian=enUnknown; | ||
278 | targetArch=arUnknown; | ||
279 | |||
280 | mi_frames *res=gmi_target_attach(h,pid); | ||
281 | if (res) | ||
282 | { | ||
283 | state=stopped; | ||
284 | |||
285 | /* Tell gdb to load symbols from the local copy. */ | ||
286 | if (!gmi_file_symbol_file(h,exec)) | ||
287 | { | ||
288 | mi_free_frames(res); | ||
289 | return NULL; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | return res; | ||
294 | } | ||
295 | |||
296 | /**[txh]******************************************************************** | ||
297 | |||
298 | Description: | ||
299 | Used to unselect the current target. When X11 mode it closes the auxiliar | ||
300 | terminal. For remote debugging it uses "detach". Can be called when in | ||
301 | "target_specified" state. On success it changes to "connected" state. | ||
302 | |||
303 | Return: !=0 OK | ||
304 | |||
305 | ***************************************************************************/ | ||
306 | |||
307 | int MIDebugger::TargetUnselect() | ||
308 | { | ||
309 | switch (mode) | ||
310 | { | ||
311 | case dmX11: | ||
312 | case dmLinux: | ||
313 | if (state!=target_specified) | ||
314 | return 0; | ||
315 | if (aux_tty) | ||
316 | { | ||
317 | gmi_end_aux_term(aux_tty); | ||
318 | aux_tty=NULL; | ||
319 | } | ||
320 | break; | ||
321 | case dmPID: | ||
322 | case dmRemote: | ||
323 | if (state!=target_specified) | ||
324 | { | ||
325 | if (state!=stopped || !gmi_target_detach(h)) | ||
326 | return 0; | ||
327 | } | ||
328 | break; | ||
329 | } | ||
330 | state=connected; | ||
331 | return 1; | ||
332 | } | ||
333 | |||
334 | /**[txh]******************************************************************** | ||
335 | |||
336 | Description: | ||
337 | Starts running the program. You should set breakpoint before it. Can be | ||
338 | called when state is "target_specified". On success will change to "running" | ||
339 | state. After it you should poll for async responses using @x{::Poll}. The | ||
340 | program can stop for many reasons asynchronously and also exit. This | ||
341 | information is known using Poll. You can stop the program using @x{::Stop}. | ||
342 | |||
343 | Return: !=0 OK. | ||
344 | |||
345 | ***************************************************************************/ | ||
346 | |||
347 | int MIDebugger::Run() | ||
348 | { | ||
349 | if (state!=target_specified) | ||
350 | return 0; | ||
351 | |||
352 | int res; | ||
353 | if (preRun) | ||
354 | res=gmi_exec_continue(h); | ||
355 | else | ||
356 | res=gmi_exec_run(h); | ||
357 | if (res) | ||
358 | state=running; | ||
359 | |||
360 | return res; | ||
361 | } | ||
362 | |||
363 | /**[txh]******************************************************************** | ||
364 | |||
365 | Description: | ||
366 | Stops the program execution. GDB sends an interrupt signal to the program. | ||
367 | Can be called when the state is "running". It won't switch to "stopped" | ||
368 | state automatically. Instead you must poll for async events and wait for a | ||
369 | stopped notification. After it you can call @x{::Continue} to resume | ||
370 | execution. | ||
371 | |||
372 | Return: | ||
373 | Example: !=0 OK | ||
374 | |||
375 | ***************************************************************************/ | ||
376 | |||
377 | int MIDebugger::Stop() | ||
378 | { | ||
379 | if (state!=running) | ||
380 | return 0; | ||
381 | return gmi_exec_interrupt(h); | ||
382 | } | ||
383 | |||
384 | /**[txh]******************************************************************** | ||
385 | |||
386 | Description: | ||
387 | Polls gdb looking for async responses. Currently it just looks for | ||
388 | "stopped" messages. You must call it when the state is "running". But the | ||
389 | function will poll gdb even if the state isn't "running". When a stopped | ||
390 | message is received the state changes to stopped or target_specified (the | ||
391 | last is when we get some exit). | ||
392 | |||
393 | Return: !=0 if we got a response. The @var{rs} pointer will point to an | ||
394 | mi_stop structure if we got it or will be NULL if we didn't. | ||
395 | |||
396 | ***************************************************************************/ | ||
397 | |||
398 | int MIDebugger::Poll(mi_stop *&rs) | ||
399 | { | ||
400 | if (state==disconnected || !mi_get_response(h)) | ||
401 | return 0; | ||
402 | |||
403 | mi_stop *res=mi_res_stop(h); | ||
404 | if (res) | ||
405 | { | ||
406 | if (res->reason==sr_exited_signalled || | ||
407 | res->reason==sr_exited || | ||
408 | res->reason==sr_exited_normally) | ||
409 | // When we use a PID the exit makes it invalid, so we don't have a | ||
410 | // valid target to re-run. | ||
411 | state=mode==dmPID ? connected : target_specified; | ||
412 | else | ||
413 | state=stopped; | ||
414 | if (res->reason==sr_unknown && waitingTempBkpt) | ||
415 | { | ||
416 | waitingTempBkpt=0; | ||
417 | res->reason=sr_bkpt_hit; | ||
418 | } | ||
419 | } | ||
420 | else | ||
421 | {// We got an error. It looks like most async commands returns running even | ||
422 | // before they are sure the process is running. Latter we get the real | ||
423 | // error. So I'm assuming the program is stopped. | ||
424 | // Lamentably -target-exec-status isn't implemented and even in this case | ||
425 | // if the program is really running as real async isn't implemented it | ||
426 | // will fail anyways. | ||
427 | if (state==running) | ||
428 | state=stopped; | ||
429 | } | ||
430 | rs=res; | ||
431 | return 1; | ||
432 | } | ||
433 | |||
434 | /**[txh]******************************************************************** | ||
435 | |||
436 | Description: | ||
437 | Resumes execution after the program "stopped". Can be called when the state | ||
438 | is stopped. On success will change to "running" state. | ||
439 | |||
440 | Return: !=0 OK | ||
441 | |||
442 | ***************************************************************************/ | ||
443 | |||
444 | int MIDebugger::Continue() | ||
445 | { | ||
446 | if (state!=stopped) | ||
447 | return 0; | ||
448 | int res=gmi_exec_continue(h); | ||
449 | if (res) | ||
450 | state=running; | ||
451 | return res; | ||
452 | } | ||
453 | |||
454 | /**[txh]******************************************************************** | ||
455 | |||
456 | Description: | ||
457 | Starts program execution or resumes it. When the state is target_specified | ||
458 | it calls @x{::Run} otherwise it uses @x{::Continue}. Can be called when the | ||
459 | state is "target_specified" or "stopped". On success will change to | ||
460 | "running" state. | ||
461 | |||
462 | Return: !=0 OK | ||
463 | |||
464 | ***************************************************************************/ | ||
465 | |||
466 | int MIDebugger::RunOrContinue() | ||
467 | { | ||
468 | if (state==target_specified) | ||
469 | return Run(); | ||
470 | return Continue(); | ||
471 | } | ||
472 | |||
473 | /**[txh]******************************************************************** | ||
474 | |||
475 | Description: | ||
476 | Kills the program you are debugging. Can be called when the state is | ||
477 | "stopped" or "running". On success changes the state to "target_specified". | ||
478 | Note that if you want to restart the program you can just call @x{::Run} and | ||
479 | if you want to just stop the program call @x{::Stop}. | ||
480 | |||
481 | Return: !=0 OK | ||
482 | |||
483 | ***************************************************************************/ | ||
484 | |||
485 | int MIDebugger::Kill() | ||
486 | { | ||
487 | if (state!=stopped && state!=running) | ||
488 | return 0; | ||
489 | /* GDB/MI doesn't implement it (yet), so we use the regular kill. */ | ||
490 | /* Ensure confirm is off. */ | ||
491 | char *prev=gmi_gdb_show(h,"confirm"); | ||
492 | if (!prev) | ||
493 | return 0; | ||
494 | if (strcmp(prev,"off")) | ||
495 | { | ||
496 | if (!gmi_gdb_set(h,"confirm","off")) | ||
497 | { | ||
498 | free(prev); | ||
499 | return 0; | ||
500 | } | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | free(prev); | ||
505 | prev=NULL; | ||
506 | } | ||
507 | /* Do the kill. */ | ||
508 | int res=gmi_exec_kill(h); | ||
509 | /* Revert confirm option if needed. */ | ||
510 | if (prev) | ||
511 | { | ||
512 | gmi_gdb_set(h,"confirm",prev); | ||
513 | free(prev); | ||
514 | } | ||
515 | |||
516 | if (res) | ||
517 | state=target_specified; | ||
518 | |||
519 | return res; | ||
520 | } | ||
521 | |||
522 | /**[txh]******************************************************************** | ||
523 | |||
524 | Description: | ||
525 | Inserts a breakpoint at @var{file} and @var{line}. Can be called when the | ||
526 | state is "stopped" or "target_specified". | ||
527 | |||
528 | Return: An mi_bkpt structure or NULL if error. | ||
529 | |||
530 | ***************************************************************************/ | ||
531 | |||
532 | mi_bkpt *MIDebugger::Breakpoint(const char *file, int line) | ||
533 | { | ||
534 | if (state!=stopped && state!=target_specified) | ||
535 | return NULL; | ||
536 | return gmi_break_insert(h,file,line); | ||
537 | } | ||
538 | |||
539 | /**[txh]******************************************************************** | ||
540 | |||
541 | Description: | ||
542 | Inserts a breakpoint at @var{where}, all options available. Can be called | ||
543 | when the state is "stopped" or "target_specified". | ||
544 | |||
545 | Return: An mi_bkpt structure or NULL if error. | ||
546 | |||
547 | ***************************************************************************/ | ||
548 | |||
549 | mi_bkpt *MIDebugger::Breakpoint(const char *where, bool temporary, | ||
550 | const char *cond, int count, int thread, | ||
551 | bool hard_assist) | ||
552 | { | ||
553 | if (state!=stopped && state!=target_specified) | ||
554 | return NULL; | ||
555 | return gmi_break_insert_full(h,temporary,hard_assist,cond,count,thread,where); | ||
556 | } | ||
557 | |||
558 | |||
559 | const int maxWhere=PATH_MAX+256; | ||
560 | |||
561 | mi_bkpt *MIDebugger::Breakpoint(mi_bkpt *b) | ||
562 | { | ||
563 | if (state!=stopped && state!=target_specified) | ||
564 | return NULL; | ||
565 | |||
566 | char buf[maxWhere]; | ||
567 | buf[0]=0; | ||
568 | switch (b->mode) | ||
569 | { | ||
570 | case m_file_line: | ||
571 | snprintf(buf,maxWhere,"%s:%d",b->file,b->line); | ||
572 | break; | ||
573 | case m_function: | ||
574 | snprintf(buf,maxWhere,"%s",b->func); | ||
575 | break; | ||
576 | case m_file_function: | ||
577 | snprintf(buf,maxWhere,"%s:%s",b->file,b->func); | ||
578 | break; | ||
579 | case m_address: | ||
580 | snprintf(buf,maxWhere,"*%p",b->addr); | ||
581 | break; | ||
582 | } | ||
583 | return Breakpoint(buf,b->disp==d_del,b->cond,b->ignore,b->thread, | ||
584 | b->type==t_hw); | ||
585 | } | ||
586 | |||
587 | /**[txh]******************************************************************** | ||
588 | |||
589 | Description: | ||
590 | Inserts a breakpoint at @var{file} and @var{line} all options available. | ||
591 | Can be called when the state is "stopped" or "target_specified". | ||
592 | |||
593 | Return: An mi_bkpt structure or NULL if error. | ||
594 | |||
595 | ***************************************************************************/ | ||
596 | |||
597 | mi_bkpt *MIDebugger::BreakpointFull(const char *file, int line, | ||
598 | bool temporary, const char *cond, | ||
599 | int count, int thread, bool hard_assist) | ||
600 | { | ||
601 | if (state!=stopped && state!=target_specified) | ||
602 | return NULL; | ||
603 | return gmi_break_insert_full_fl(h,file,line,temporary,hard_assist,cond, | ||
604 | count,thread); | ||
605 | } | ||
606 | |||
607 | /**[txh]******************************************************************** | ||
608 | |||
609 | Description: | ||
610 | Removes the specified breakpoint. It doesn't free the structure. Can be | ||
611 | called when the state is "stopped" or "target_specified". | ||
612 | |||
613 | Return: !=0 OK | ||
614 | |||
615 | ***************************************************************************/ | ||
616 | |||
617 | int MIDebugger::BreakDelete(mi_bkpt *b) | ||
618 | { | ||
619 | if ((state!=stopped && state!=target_specified) || !b) | ||
620 | return 0; | ||
621 | return gmi_break_delete(h,b->number); | ||
622 | } | ||
623 | |||
624 | /**[txh]******************************************************************** | ||
625 | |||
626 | Description: | ||
627 | Inserts a watchpoint for the specified expression. Can be called when the | ||
628 | state is "stopped" or "target_specified". | ||
629 | |||
630 | Return: An mi_wp structure or NULL if error. | ||
631 | |||
632 | ***************************************************************************/ | ||
633 | |||
634 | mi_wp *MIDebugger::Watchpoint(enum mi_wp_mode mode, const char *exp) | ||
635 | { | ||
636 | if (state!=stopped && state!=target_specified) | ||
637 | return NULL; | ||
638 | return gmi_break_watch(h,mode,exp); | ||
639 | } | ||
640 | |||
641 | /**[txh]******************************************************************** | ||
642 | |||
643 | Description: | ||
644 | Removes the specified watchpoint. It doesn't free the structure. Can be | ||
645 | called when the state is "stopped" or "target_specified". | ||
646 | |||
647 | Return: !=0 OK | ||
648 | |||
649 | ***************************************************************************/ | ||
650 | |||
651 | int MIDebugger::WatchDelete(mi_wp *w) | ||
652 | { | ||
653 | if ((state!=stopped && state!=target_specified) || !w) | ||
654 | return 0; | ||
655 | return gmi_break_delete(h,w->number); | ||
656 | } | ||
657 | |||
658 | /**[txh]******************************************************************** | ||
659 | |||
660 | Description: | ||
661 | Puts a temporal breakpoint in main function and starts running. Can be | ||
662 | called when the state is "target_specified". If successful the state will | ||
663 | change to "running". | ||
664 | |||
665 | Return: !=0 OK | ||
666 | |||
667 | ***************************************************************************/ | ||
668 | |||
669 | int MIDebugger::RunToMain() | ||
670 | { | ||
671 | if (state!=target_specified) | ||
672 | return 0; | ||
673 | mi_bkpt *b=Breakpoint(mi_get_main_func(),true); | ||
674 | if (!b) | ||
675 | return 0; | ||
676 | mi_free_bkpt(b); | ||
677 | waitingTempBkpt=1; | ||
678 | return Run(); | ||
679 | } | ||
680 | |||
681 | /**[txh]******************************************************************** | ||
682 | |||
683 | Description: | ||
684 | Executes upto the next line, doesn't follow function calls. The @var{inst} | ||
685 | argument is for assembler. If the state is "target_specified" it will go to | ||
686 | the first line in the main function. If the state is "stopped" will use the | ||
687 | next command. If successfully the state will change to "running". | ||
688 | |||
689 | Return: !=0 OK | ||
690 | |||
691 | ***************************************************************************/ | ||
692 | |||
693 | int MIDebugger::StepOver(bool inst) | ||
694 | { | ||
695 | int res=0; | ||
696 | |||
697 | if (state==target_specified) | ||
698 | {// We aren't running | ||
699 | // Walk to main | ||
700 | return RunToMain(); | ||
701 | } | ||
702 | if (state==stopped) | ||
703 | { | ||
704 | if (inst) | ||
705 | res=gmi_exec_next_instruction(h); | ||
706 | else | ||
707 | res=gmi_exec_next(h); | ||
708 | if (res) | ||
709 | state=running; | ||
710 | } | ||
711 | return res; | ||
712 | } | ||
713 | |||
714 | /**[txh]******************************************************************** | ||
715 | |||
716 | Description: | ||
717 | Executes until the specified point. If the state is "target_specified" it | ||
718 | uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. | ||
719 | Fails for any other state. | ||
720 | |||
721 | Return: !=0 OK | ||
722 | |||
723 | ***************************************************************************/ | ||
724 | |||
725 | int MIDebugger::GoTo(const char *file, int line) | ||
726 | { | ||
727 | int res=0; | ||
728 | |||
729 | if (state==target_specified) | ||
730 | {// We aren't running | ||
731 | // Use a temporal breakpoint | ||
732 | int l=strlen(file)+32; | ||
733 | char buf[l]; | ||
734 | snprintf(buf,l,"%s:%d",file,line); | ||
735 | mi_bkpt *b=Breakpoint(buf,true); | ||
736 | if (b) | ||
737 | { | ||
738 | mi_free_bkpt(b); | ||
739 | waitingTempBkpt=1; | ||
740 | res=Run(); | ||
741 | } | ||
742 | } | ||
743 | else if (state==stopped) | ||
744 | { | ||
745 | res=gmi_exec_until(h,file,line); | ||
746 | if (res) | ||
747 | state=running; | ||
748 | } | ||
749 | return res; | ||
750 | } | ||
751 | |||
752 | /**[txh]******************************************************************** | ||
753 | |||
754 | Description: | ||
755 | Executes until the specified point. If the state is "target_specified" it | ||
756 | uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. | ||
757 | Fails for any other state. | ||
758 | |||
759 | Return: !=0 OK | ||
760 | |||
761 | ***************************************************************************/ | ||
762 | |||
763 | int MIDebugger::GoTo(void *addr) | ||
764 | { | ||
765 | int res=0; | ||
766 | |||
767 | if (state==target_specified) | ||
768 | {// We aren't running | ||
769 | // Use a temporal breakpoint | ||
770 | char buf[32]; | ||
771 | snprintf(buf,32,"*%p",addr); | ||
772 | mi_bkpt *b=Breakpoint(buf,true); | ||
773 | if (b) | ||
774 | { | ||
775 | mi_free_bkpt(b); | ||
776 | waitingTempBkpt=1; | ||
777 | res=Run(); | ||
778 | } | ||
779 | } | ||
780 | else if (state==stopped) | ||
781 | { | ||
782 | res=gmi_exec_until_addr(h,addr); | ||
783 | if (res) | ||
784 | state=running; | ||
785 | } | ||
786 | return res; | ||
787 | } | ||
788 | |||
789 | |||
790 | /**[txh]******************************************************************** | ||
791 | |||
792 | Description: | ||
793 | Resumes execution until the end of the current funtion is reached. Only | ||
794 | usable when we are in the "stopped" state. | ||
795 | |||
796 | Return: !=0 OK | ||
797 | |||
798 | ***************************************************************************/ | ||
799 | |||
800 | int MIDebugger::FinishFun() | ||
801 | { | ||
802 | if (state!=stopped) | ||
803 | return 0; | ||
804 | int res=gmi_exec_finish(h); | ||
805 | if (res) | ||
806 | state=running; | ||
807 | return res; | ||
808 | } | ||
809 | |||
810 | /**[txh]******************************************************************** | ||
811 | |||
812 | Description: | ||
813 | Returns immediately. Only usable when we are in the "stopped" state. | ||
814 | |||
815 | Return: !=NULL OK, the returned frame is the current location. That's a | ||
816 | synchronous function. | ||
817 | |||
818 | ***************************************************************************/ | ||
819 | |||
820 | mi_frames *MIDebugger::ReturnNow() | ||
821 | { | ||
822 | if (state!=stopped) | ||
823 | return 0; | ||
824 | return gmi_exec_return(h); | ||
825 | } | ||
826 | |||
827 | /**[txh]******************************************************************** | ||
828 | |||
829 | Description: | ||
830 | Returns the current list of frames. | ||
831 | |||
832 | Return: !=NULL OK, the list of frames is returned. | ||
833 | |||
834 | ***************************************************************************/ | ||
835 | |||
836 | mi_frames *MIDebugger::CallStack(bool args) | ||
837 | { | ||
838 | if (state!=stopped) | ||
839 | return 0; | ||
840 | mi_frames *fr1=gmi_stack_list_frames(h); | ||
841 | if (fr1 && args) | ||
842 | {// Get the function arguments | ||
843 | mi_frames *fr2=gmi_stack_list_arguments(h,1); | ||
844 | if (fr2) | ||
845 | {// Transfer them to the other list | ||
846 | mi_frames *p=fr1, *p2=fr2; | ||
847 | while (p2 && p) | ||
848 | { | ||
849 | p->args=p2->args; | ||
850 | p2->args=NULL; | ||
851 | p2=p2->next; | ||
852 | p=p->next; | ||
853 | } | ||
854 | mi_free_frames(fr2); | ||
855 | } | ||
856 | } | ||
857 | return fr1; | ||
858 | } | ||
859 | |||
860 | /**[txh]******************************************************************** | ||
861 | |||
862 | Description: | ||
863 | Executes upto the next line, it follows function calls. The @var{inst} | ||
864 | argument is for assembler. If the state is "target_specified" it will go to | ||
865 | the first line in the main function. If the state is "stopped" will use the | ||
866 | next command. If successfully the state will change to "running". | ||
867 | |||
868 | Return: !=0 OK | ||
869 | |||
870 | ***************************************************************************/ | ||
871 | |||
872 | int MIDebugger::TraceInto(bool inst) | ||
873 | { | ||
874 | int res=0; | ||
875 | |||
876 | if (state==target_specified) | ||
877 | {// We aren't running | ||
878 | // Walk to main | ||
879 | return RunToMain(); | ||
880 | } | ||
881 | if (state==stopped) | ||
882 | { | ||
883 | if (inst) | ||
884 | res=gmi_exec_step_instruction(h); | ||
885 | else | ||
886 | res=gmi_exec_step(h); | ||
887 | if (res) | ||
888 | state=running; | ||
889 | } | ||
890 | return res; | ||
891 | } | ||
892 | |||
893 | /**[txh]******************************************************************** | ||
894 | |||
895 | Description: | ||
896 | Evaluates the provided expression. If we get an error the error | ||
897 | description is returned instead. Can't be called if "disconnected" or | ||
898 | "running". | ||
899 | |||
900 | Return: The result of the expression (use free) or NULL. | ||
901 | |||
902 | ***************************************************************************/ | ||
903 | |||
904 | char *MIDebugger::EvalExpression(const char *exp) | ||
905 | { | ||
906 | if (state==disconnected || | ||
907 | state==running) // No async :-( | ||
908 | return NULL; | ||
909 | // Evaluate it | ||
910 | mi_error=MI_OK; | ||
911 | char *res=gmi_data_evaluate_expression(h,exp); | ||
912 | if (!res && mi_error_from_gdb) | ||
913 | {// Not valid, return the error | ||
914 | res=strdup(mi_error_from_gdb); | ||
915 | } | ||
916 | return res; | ||
917 | } | ||
918 | |||
919 | /**[txh]******************************************************************** | ||
920 | |||
921 | Description: | ||
922 | Modifies the provided expression. If we get an error the error | ||
923 | description is returned instead. Can't be called if "disconnected" or | ||
924 | "running". | ||
925 | |||
926 | Return: The result of the expression (use free) or NULL. | ||
927 | |||
928 | ***************************************************************************/ | ||
929 | |||
930 | char *MIDebugger::ModifyExpression(char *exp, char *newVal) | ||
931 | { | ||
932 | if (state==disconnected || | ||
933 | state==running) // No async :-( | ||
934 | return NULL; | ||
935 | // Create an assignment | ||
936 | int l1=strlen(exp); | ||
937 | int l2=strlen(newVal); | ||
938 | char b[l1+l2+2], *s=b; | ||
939 | memcpy(s,exp,l1); | ||
940 | s+=l1; | ||
941 | *s='='; | ||
942 | memcpy(++s,newVal,l2); | ||
943 | s[l2]=0; | ||
944 | // Evaluate it | ||
945 | char *res=gmi_data_evaluate_expression(h,b); | ||
946 | if (!res && mi_error_from_gdb) | ||
947 | {// Not valid, return the error | ||
948 | res=strdup(mi_error_from_gdb); | ||
949 | } | ||
950 | return res; | ||
951 | } | ||
952 | |||
953 | /**[txh]******************************************************************** | ||
954 | |||
955 | Description: | ||
956 | Sends a command to gdb. | ||
957 | |||
958 | Return: !=0 OK | ||
959 | |||
960 | ***************************************************************************/ | ||
961 | |||
962 | int MIDebugger::Send(const char *command) | ||
963 | { | ||
964 | if (state==disconnected || | ||
965 | state==running) // No async :-( | ||
966 | return 0; | ||
967 | // TODO: detect and use -interpreter-exec? | ||
968 | mi_send(h,"%s\n",command); | ||
969 | return mi_res_simple_done(h); | ||
970 | } | ||
971 | |||
972 | |||
973 | /**[txh]******************************************************************** | ||
974 | |||
975 | Description: | ||
976 | Fills the type and value fields of the mi_gvar provided list. | ||
977 | |||
978 | Return: !=0 OK | ||
979 | |||
980 | ***************************************************************************/ | ||
981 | |||
982 | int MIDebugger::FillTypeVal(mi_gvar *var) | ||
983 | { | ||
984 | while (var) | ||
985 | { | ||
986 | if (!var->type && !gmi_var_info_type(h,var)) | ||
987 | return 0; | ||
988 | if (!var->value && !gmi_var_evaluate_expression(h,var)) | ||
989 | return 0; | ||
990 | var=var->next; | ||
991 | } | ||
992 | return 1; | ||
993 | } | ||
994 | |||
995 | int MIDebugger::FillOneTypeVal(mi_gvar *var) | ||
996 | { | ||
997 | if (!var) | ||
998 | return 0; | ||
999 | |||
1000 | int ok=1; | ||
1001 | if (!var->type && !gmi_var_info_type(h,var)) | ||
1002 | { | ||
1003 | var->type=strdup(""); | ||
1004 | ok=0; | ||
1005 | } | ||
1006 | if (!var->value && !gmi_var_evaluate_expression(h,var)) | ||
1007 | { | ||
1008 | var->value=strdup(""); | ||
1009 | ok=0; | ||
1010 | } | ||
1011 | return ok; | ||
1012 | } | ||
1013 | |||
1014 | int MIDebugger::AssigngVar(mi_gvar *var, const char *exp) | ||
1015 | { | ||
1016 | if (state!=stopped) | ||
1017 | return 0; | ||
1018 | return gmi_var_assign(h,var,exp); | ||
1019 | } | ||
1020 | |||
1021 | char *MIDebugger::Show(const char *var) | ||
1022 | { | ||
1023 | if (state==running || state==disconnected) | ||
1024 | return 0; | ||
1025 | // GDB 5.x doesn't reply all in the response record, just to the console :-( | ||
1026 | h->catch_console=1; | ||
1027 | if (h->catched_console) | ||
1028 | { | ||
1029 | free(h->catched_console); | ||
1030 | h->catched_console=NULL; | ||
1031 | } | ||
1032 | char *res=gmi_gdb_show(h,var); | ||
1033 | h->catch_console=0; | ||
1034 | if (!res && h->catched_console) | ||
1035 | { | ||
1036 | res=h->catched_console; | ||
1037 | h->catched_console=NULL; | ||
1038 | } | ||
1039 | return res; | ||
1040 | } | ||
1041 | |||
1042 | MIDebugger::endianType MIDebugger::GetTargetEndian() | ||
1043 | { | ||
1044 | if (targetEndian!=enUnknown) | ||
1045 | return targetEndian; | ||
1046 | if (state!=stopped && state!=target_specified) | ||
1047 | return enUnknown; | ||
1048 | |||
1049 | char *end=Show("endian"); | ||
1050 | if (end) | ||
1051 | { | ||
1052 | if (strstr(end,"big")) | ||
1053 | targetEndian=enBig; | ||
1054 | else if (strstr(end,"little")) | ||
1055 | targetEndian=enLittle; | ||
1056 | free(end); | ||
1057 | } | ||
1058 | return targetEndian; | ||
1059 | } | ||
1060 | |||
1061 | MIDebugger::archType MIDebugger::GetTargetArchitecture() | ||
1062 | { | ||
1063 | if (targetArch!=arUnknown) | ||
1064 | return targetArch; | ||
1065 | if (state!=stopped && state!=target_specified) | ||
1066 | return arUnknown; | ||
1067 | |||
1068 | char *end=Show("architecture"); | ||
1069 | if (end) | ||
1070 | { | ||
1071 | if (strstr(end,"i386")) | ||
1072 | targetArch=arIA32; | ||
1073 | else if (strstr(end,"sparc")) | ||
1074 | targetArch=arSPARC; | ||
1075 | else if (strstr(end,"pic14")) | ||
1076 | targetArch=arPIC14; | ||
1077 | else if (strstr(end,"avr")) | ||
1078 | targetArch=arAVR; | ||
1079 | free(end); | ||
1080 | } | ||
1081 | return targetArch; | ||
1082 | } | ||
1083 | |||
1084 | int MIDebugger::GetErrorNumberSt() | ||
1085 | { | ||
1086 | if (mi_error==MI_GDB_DIED) | ||
1087 | { | ||
1088 | state=target_specified; | ||
1089 | TargetUnselect(); | ||
1090 | state=connected; | ||
1091 | Disconnect(); | ||
1092 | } | ||
1093 | return mi_error; | ||
1094 | } | ||
1095 | |||
1096 | int MIDebugger::UpdateRegisters(mi_chg_reg *regs) | ||
1097 | { | ||
1098 | int updated=0; | ||
1099 | mi_chg_reg *chg=GetChangedRegisters(); | ||
1100 | if (chg) | ||
1101 | { | ||
1102 | mi_chg_reg *r=regs, *c; | ||
1103 | while (r) | ||
1104 | { | ||
1105 | c=chg; | ||
1106 | while (c && c->reg!=r->reg) | ||
1107 | c=c->next; | ||
1108 | if (c) | ||
1109 | { | ||
1110 | r->updated=1; | ||
1111 | free(r->val); | ||
1112 | r->val=c->val; | ||
1113 | c->val=NULL; | ||
1114 | updated++; | ||
1115 | } | ||
1116 | else | ||
1117 | r->updated=0; | ||
1118 | r=r->next; | ||
1119 | } | ||
1120 | } | ||
1121 | return updated; | ||
1122 | } | ||
1123 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Data manipulation. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Data manipulation" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | |||
13 | -data-disassemble Yes | ||
14 | -data-evaluate-expression Yes | ||
15 | -data-list-changed-registers No | ||
16 | -data-list-register-names Yes | ||
17 | -data-list-register-values No | ||
18 | -data-read-memory No | ||
19 | -display-delete N.A. (delete display) | ||
20 | -display-disable N.A. (disable display) | ||
21 | -display-enable N.A. (enable display) | ||
22 | -display-insert N.A. (display) | ||
23 | -display-list N.A. (info display) | ||
24 | -environment-cd No | ||
25 | -environment-directory Yes, MI v1 implementation | ||
26 | -environment-path No | ||
27 | @</pre> | ||
28 | |||
29 | Notes:@p | ||
30 | |||
31 | 1) -display* aren't implemented. You can use CLI command display, but the | ||
32 | results are sent to the console. So it looks like the best is to manually | ||
33 | use -data-evaluate-expression to emulate it.@p | ||
34 | |||
35 | 2) GDB bug mi/1770: Affects gdb<=6.2, when you ask for the names of the | ||
36 | registers you get it plus the name of the "pseudo-registers", but if you | ||
37 | try to get the value of a pseudo-register you get an error saying the | ||
38 | register number is invalid. I reported to gdb-patches@sources.redhat.com | ||
39 | on 2004/08/25 and as I didn't get any answer I filled a bug report on | ||
40 | 2004/09/02. The patch to fix this annoying bug is: | ||
41 | |||
42 | Index: gdb/mi/mi-main.c | ||
43 | =================================================================== | ||
44 | RCS file: /cvs/src/src/gdb/mi/mi-main.c,v | ||
45 | retrieving revision 1.64 | ||
46 | diff -u -r1.64 mi-main.c | ||
47 | --- gdb/mi/mi-main.c 3 Aug 2004 00:57:27 -0000 1.64 | ||
48 | +++ gdb/mi/mi-main.c 25 Aug 2004 14:12:50 -0000 | ||
49 | @@ -423,7 +423,7 @@ | ||
50 | case, some entries of REGISTER_NAME will change depending upon | ||
51 | the particular processor being debugged. | ||
52 | |||
53 | - numregs = NUM_REGS; | ||
54 | + numregs = NUM_REGS + NUM_PSEUDO_REGS; | ||
55 | |||
56 | if (argc == 0) | ||
57 | { | ||
58 | ---- | ||
59 | |||
60 | Note I had to remove an end of comment in the patch to include it here. | ||
61 | This bug forced me to create another set of functions. The only way is to | ||
62 | first get the values and then the names. | ||
63 | Fixed by Changelog entry: | ||
64 | |||
65 | 2004-09-12 Salvador E. Tropea <set@users.sf.net> | ||
66 | Andrew Cagney <cagney@gnu.org> | ||
67 | |||
68 | * mi/mi-main.c (mi_cmd_data_list_changed_registers) | ||
69 | (mi_cmd_data_list_register_values) | ||
70 | (mi_cmd_data_write_register_values): Include the PSEUDO_REGS in | ||
71 | the register number computation. | ||
72 | |||
73 | ***************************************************************************/ | ||
74 | |||
75 | #include "mi_gdb.h" | ||
76 | |||
77 | /* Low level versions. */ | ||
78 | |||
79 | void mi_data_evaluate_expression(mi_h *h, const char *expression) | ||
80 | { | ||
81 | mi_send(h,"-data-evaluate-expression \"%s\"\n",expression); | ||
82 | } | ||
83 | |||
84 | void mi_dir(mi_h *h, const char *path) | ||
85 | { | ||
86 | if (h->version>=MI_VERSION2U(2,0,0)) | ||
87 | {// MI v2 | ||
88 | if (path) | ||
89 | mi_send(h,"-environment-directory \"%s\"\n",path); | ||
90 | else | ||
91 | mi_send(h,"-environment-directory -r\n"); | ||
92 | } | ||
93 | else | ||
94 | { | ||
95 | mi_send(h,"-environment-directory %s\n",path ? path : ""); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | void mi_data_read_memory_hx(mi_h *h, const char *exp, unsigned ws, | ||
100 | unsigned c, int convAddr) | ||
101 | { | ||
102 | if (convAddr) | ||
103 | mi_send(h,"-data-read-memory \"&%s\" x %d 1 %d\n",exp,ws,c); | ||
104 | else | ||
105 | mi_send(h,"-data-read-memory \"%s\" x %d 1 %d\n",exp,ws,c); | ||
106 | } | ||
107 | |||
108 | void mi_data_disassemble_se(mi_h *h, const char *start, const char *end, | ||
109 | int mode) | ||
110 | { | ||
111 | mi_send(h,"-data-disassemble -s \"%s\" -e \"%s\" -- %d\n",start,end,mode); | ||
112 | } | ||
113 | |||
114 | void mi_data_disassemble_fl(mi_h *h, const char *file, int line, int lines, | ||
115 | int mode) | ||
116 | { | ||
117 | mi_send(h,"-data-disassemble -f \"%s\" -l %d -n %d -- %d\n",file,line,lines, | ||
118 | mode); | ||
119 | } | ||
120 | |||
121 | void mi_data_list_register_names(mi_h *h) | ||
122 | { | ||
123 | mi_send(h,"-data-list-register-names\n"); | ||
124 | } | ||
125 | |||
126 | void mi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) | ||
127 | { | ||
128 | mi_send(h,"-data-list-register-names "); | ||
129 | while (l) | ||
130 | { | ||
131 | mi_send(h,"%d ",l->reg); | ||
132 | l=l->next; | ||
133 | } | ||
134 | mi_send(h,"\n"); | ||
135 | } | ||
136 | |||
137 | void mi_data_list_changed_registers(mi_h *h) | ||
138 | { | ||
139 | mi_send(h,"-data-list-changed-registers\n"); | ||
140 | } | ||
141 | |||
142 | void mi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) | ||
143 | { | ||
144 | mi_send(h,"-data-list-register-values %c ",mi_format_enum_to_char(fmt)); | ||
145 | while (l) | ||
146 | { | ||
147 | mi_send(h,"%d ",l->reg); | ||
148 | l=l->next; | ||
149 | } | ||
150 | mi_send(h,"\n"); | ||
151 | } | ||
152 | |||
153 | /* High level versions. */ | ||
154 | |||
155 | /**[txh]******************************************************************** | ||
156 | |||
157 | Description: | ||
158 | Evaluate an expression. Returns a parsed tree. | ||
159 | |||
160 | Command: -data-evaluate-expression | ||
161 | Return: The resulting value (as plain text) or NULL on error. | ||
162 | |||
163 | ***************************************************************************/ | ||
164 | |||
165 | char *gmi_data_evaluate_expression(mi_h *h, const char *expression) | ||
166 | { | ||
167 | mi_data_evaluate_expression(h,expression); | ||
168 | return mi_res_value(h); | ||
169 | } | ||
170 | |||
171 | /**[txh]******************************************************************** | ||
172 | |||
173 | Description: | ||
174 | Path for sources. You must use it to indicate where are the sources for | ||
175 | the program to debug. Only the MI v1 implementation is available. | ||
176 | |||
177 | Command: -environment-directory | ||
178 | Return: !=0 OK | ||
179 | |||
180 | ***************************************************************************/ | ||
181 | |||
182 | int gmi_dir(mi_h *h, const char *path) | ||
183 | { | ||
184 | mi_dir(h,path); | ||
185 | return mi_res_simple_done(h); | ||
186 | } | ||
187 | |||
188 | int gmi_read_memory(mi_h *h, const char *exp, unsigned size, | ||
189 | unsigned char *dest, int *na, int convAddr, | ||
190 | unsigned long *addr) | ||
191 | { | ||
192 | mi_data_read_memory_hx(h,exp,1,size,convAddr); | ||
193 | return mi_get_read_memory(h,dest,1,na,addr); | ||
194 | } | ||
195 | |||
196 | mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, | ||
197 | const char *end, int mode) | ||
198 | { | ||
199 | mi_data_disassemble_se(h,start,end,mode); | ||
200 | return mi_get_asm_insns(h); | ||
201 | } | ||
202 | |||
203 | mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, | ||
204 | int lines, int mode) | ||
205 | { | ||
206 | mi_data_disassemble_fl(h,file,line,lines,mode); | ||
207 | return mi_get_asm_insns(h); | ||
208 | } | ||
209 | |||
210 | // Affected by gdb bug mi/1770 | ||
211 | mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many) | ||
212 | { | ||
213 | mi_data_list_register_names(h); | ||
214 | return mi_get_list_registers(h,how_many); | ||
215 | } | ||
216 | |||
217 | int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) | ||
218 | { | ||
219 | mi_data_list_register_names_l(h,l); | ||
220 | return mi_get_list_registers_l(h,l); | ||
221 | } | ||
222 | |||
223 | mi_chg_reg *gmi_data_list_changed_registers(mi_h *h) | ||
224 | { | ||
225 | mi_error=MI_OK; | ||
226 | mi_data_list_changed_registers(h); | ||
227 | return mi_get_list_changed_regs(h); | ||
228 | } | ||
229 | |||
230 | int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) | ||
231 | { | ||
232 | mi_data_list_register_values(h,fmt,l); | ||
233 | return mi_get_reg_values(h,l); | ||
234 | } | ||
235 | |||
236 | mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many) | ||
237 | { | ||
238 | mi_data_list_register_values(h,fmt,NULL); | ||
239 | return mi_get_reg_values_l(h,how_many); | ||
240 | } | ||
241 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Error. | ||
7 | Comment: | ||
8 | Translates error numbers into messages. | ||
9 | |||
10 | ***************************************************************************/ | ||
11 | |||
12 | #include "mi_gdb.h" | ||
13 | |||
14 | static | ||
15 | const char *error_strs[]= | ||
16 | { | ||
17 | "Ok", | ||
18 | "Out of memory", | ||
19 | "Pipe creation", | ||
20 | "Fork failed", | ||
21 | "GDB not running", | ||
22 | "Parser failed", | ||
23 | "Unknown asyn response", | ||
24 | "Unknown result response", | ||
25 | "Error from gdb", | ||
26 | "Time out in gdb response", | ||
27 | "GDB suddenly died", | ||
28 | "Can't execute X terminal", | ||
29 | "Failed to create temporal", | ||
30 | "Can't execute the debugger" | ||
31 | }; | ||
32 | |||
33 | const char *mi_get_error_str() | ||
34 | { | ||
35 | if (mi_error<0 || mi_error>MI_LAST_ERROR) | ||
36 | return "Unknown"; | ||
37 | return error_strs[mi_error]; | ||
38 | } | ||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: pseudo terminal | ||
7 | Comments: | ||
8 | Helper to find a free pseudo terminal. Use this if you need to manage | ||
9 | input *and* output to the target process. If you just need output then | ||
10 | define a handler for target output stream records (assuming that this | ||
11 | is working for your particular version of gdb). | ||
12 | Usage: | ||
13 | |||
14 | mi_pty *pty = gmi_look_for_free_pty(); | ||
15 | if (pty) gmi_target_terminal(mih, pty->slave); | ||
16 | ... | ||
17 | * reading from pty->master will get stdout from target * | ||
18 | * writing to pty->master will send to target stdin * | ||
19 | |||
20 | Note: Contributed by Greg Watson (gwatson lanl gov) | ||
21 | |||
22 | ***************************************************************************/ | ||
23 | |||
24 | #define _GNU_SOURCE | ||
25 | #include <string.h> | ||
26 | #include <stdio.h> | ||
27 | #include <unistd.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <sys/ioctl.h> | ||
30 | |||
31 | #include "mi_gdb.h" | ||
32 | |||
33 | /**[txh]******************************************************************** | ||
34 | |||
35 | Description: | ||
36 | Look for a free and usable pseudo terminal. Low level, use | ||
37 | @x{gmi_look_for_free_pty}. | ||
38 | |||
39 | Return: A file descriptor connected to the master pty and the name of the slave device, or <0 on error. | ||
40 | |||
41 | ***************************************************************************/ | ||
42 | |||
43 | #ifdef __APPLE__ | ||
44 | |||
45 | #include <util.h> | ||
46 | |||
47 | int mi_look_for_free_pty(int *master, char **slave) | ||
48 | { | ||
49 | int fdmaster; | ||
50 | int fdslave; | ||
51 | static char name[BUFSIZ]; | ||
52 | |||
53 | if (openpty(&fdmaster,&fdslave,name,NULL,NULL)<0) | ||
54 | return -1; | ||
55 | |||
56 | (void)close(fdslave); /* this will be reopened by gdb */ | ||
57 | *master=fdmaster; | ||
58 | *slave =name; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | #elif defined(__linux__) | ||
64 | |||
65 | int mi_look_for_free_pty(int *master, char **slave) | ||
66 | { | ||
67 | if ((*master=open("/dev/ptmx",O_RDWR))<0) | ||
68 | return -1; | ||
69 | if (grantpt(*master)<0 || unlockpt(*master)<0) | ||
70 | return -1; | ||
71 | *slave = ptsname(*master); | ||
72 | |||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | #else /* undefined o/s */ | ||
77 | |||
78 | int mi_look_for_free_pty(int *master, char **slave) | ||
79 | { | ||
80 | return -1; | ||
81 | } | ||
82 | #endif | ||
83 | |||
84 | /**[txh]******************************************************************** | ||
85 | |||
86 | Description: | ||
87 | Look for a free and usable pseudo terminal to be used by the child. | ||
88 | |||
89 | Return: A new mi_pty structure, you can use @x{gmi_end_pty} to | ||
90 | release it. | ||
91 | |||
92 | ***************************************************************************/ | ||
93 | |||
94 | mi_pty *gmi_look_for_free_pty() | ||
95 | { | ||
96 | int master; | ||
97 | char *slave; | ||
98 | int pty=mi_look_for_free_pty(&master,&slave); | ||
99 | mi_pty *res; | ||
100 | |||
101 | if (pty<0) | ||
102 | return NULL; | ||
103 | res=(mi_pty *)malloc(sizeof(mi_pty)); | ||
104 | if (!res) | ||
105 | return NULL; | ||
106 | res->slave=strdup(slave); | ||
107 | res->master=master; | ||
108 | return res; | ||
109 | } | ||
110 | |||
111 | void mi_free_pty(mi_pty *p) | ||
112 | { | ||
113 | if (!p) | ||
114 | return; | ||
115 | free(p->slave); | ||
116 | free(p); | ||
117 | } | ||
118 | |||
119 | /**[txh]******************************************************************** | ||
120 | |||
121 | Description: | ||
122 | Closes the pseudo termial master and releases the allocated memory. | ||
123 | |||
124 | ***************************************************************************/ | ||
125 | |||
126 | void gmi_end_pty(mi_pty *p) | ||
127 | { | ||
128 | if (!p) | ||
129 | return; | ||
130 | close(p->master); | ||
131 | mi_free_pty(p); | ||
132 | } | ||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Linux VT. | ||
7 | Comments: | ||
8 | Helper to find a free VT. That's 100% Linux specific.@p | ||
9 | The code comes from "lconsole.c" from Allegro project and was originally | ||
10 | created by Marek Habersack and then modified by George Foot. I addapted it | ||
11 | to my needs and changed license from giftware to GPL.@p | ||
12 | |||
13 | ***************************************************************************/ | ||
14 | |||
15 | #define _GNU_SOURCE | ||
16 | #include <string.h> | ||
17 | #include <stdio.h> | ||
18 | #include <unistd.h> | ||
19 | #include <fcntl.h> | ||
20 | #include <sys/ioctl.h> | ||
21 | #ifdef __APPLE__ | ||
22 | #include <util.h> | ||
23 | #endif /* __APPLE__ */ | ||
24 | |||
25 | #include "mi_gdb.h" | ||
26 | |||
27 | #if !defined(__linux__) | ||
28 | |||
29 | int mi_look_for_free_vt() | ||
30 | { | ||
31 | return -1; | ||
32 | } | ||
33 | |||
34 | mi_aux_term *gmi_look_for_free_vt() | ||
35 | { | ||
36 | return NULL; | ||
37 | } | ||
38 | |||
39 | #else | ||
40 | |||
41 | #include <linux/vt.h> | ||
42 | |||
43 | /**[txh]******************************************************************** | ||
44 | |||
45 | Description: | ||
46 | Look for a free and usable Linux VT. Low level, use | ||
47 | @x{gmi_look_for_free_vt}. | ||
48 | |||
49 | Return: The VT number or <0 on error. | ||
50 | |||
51 | ***************************************************************************/ | ||
52 | |||
53 | int mi_look_for_free_vt() | ||
54 | {/* Code from Allegro. */ | ||
55 | int tty, console_fd, fd; | ||
56 | unsigned short mask; | ||
57 | char tty_name[16]; | ||
58 | struct vt_stat vts; | ||
59 | |||
60 | /* Now we need to find a VT we can use. It must be readable and | ||
61 | * writable by us, if we're not setuid root. VT_OPENQRY itself | ||
62 | * isn't too useful because it'll only ever come up with one | ||
63 | * suggestion, with no guarrantee that we actually have access | ||
64 | * to it. | ||
65 | * | ||
66 | * At some stage I think this is a candidate for config | ||
67 | * file overriding, but for now we'll stat the first N consoles | ||
68 | * to see which ones we can write to (hopefully at least one!), | ||
69 | * so that we can use that one to do ioctls. We used to use | ||
70 | * /dev/console for that purpose but it looks like it's not | ||
71 | * always writable by enough people. | ||
72 | * | ||
73 | * Having found and opened a writable device, we query the state | ||
74 | * of the first sixteen (fifteen really) consoles, and try | ||
75 | * opening each unused one in turn. | ||
76 | */ | ||
77 | |||
78 | console_fd=open("/dev/console",O_WRONLY); | ||
79 | if (console_fd<0) | ||
80 | { | ||
81 | int n; | ||
82 | /* Try some ttys instead... */ | ||
83 | for (n=1; n<=24; n++) | ||
84 | { | ||
85 | snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",n); | ||
86 | console_fd=open(tty_name,O_WRONLY); | ||
87 | if (console_fd>=0) | ||
88 | break; | ||
89 | } | ||
90 | if (n>24) | ||
91 | return -1; | ||
92 | } | ||
93 | |||
94 | /* Get the state of the console -- in particular, the free VT field */ | ||
95 | if (ioctl(console_fd,VT_GETSTATE,&vts)) | ||
96 | return -2; | ||
97 | close(console_fd); | ||
98 | |||
99 | /* We attempt to set our euid to 0; if we were run with euid 0 to | ||
100 | * start with, we'll be able to do this now. Otherwise, we'll just | ||
101 | * ignore the error returned since it might not be a problem if the | ||
102 | * ttys we look at are owned by the user running the program. */ | ||
103 | seteuid(0); | ||
104 | |||
105 | /* tty0 is not really a console, so start counting at 2. */ | ||
106 | fd=-1; | ||
107 | for (tty=1, mask=2; mask; tty++, mask<<=1) | ||
108 | if (!(vts.v_state & mask)) | ||
109 | { | ||
110 | snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",tty); | ||
111 | fd=open(tty_name,O_RDWR); | ||
112 | if (fd!=-1) | ||
113 | { | ||
114 | close(fd); | ||
115 | break; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | seteuid(getuid()); | ||
120 | |||
121 | if (!mask) | ||
122 | return -3; | ||
123 | |||
124 | return tty; | ||
125 | } | ||
126 | |||
127 | /**[txh]******************************************************************** | ||
128 | |||
129 | Description: | ||
130 | Look for a free and usable Linux VT to be used by the child. | ||
131 | |||
132 | Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to | ||
133 | release it. | ||
134 | |||
135 | ***************************************************************************/ | ||
136 | |||
137 | mi_aux_term *gmi_look_for_free_vt() | ||
138 | { | ||
139 | int vt=mi_look_for_free_vt(); | ||
140 | mi_aux_term *res; | ||
141 | |||
142 | if (vt<0) | ||
143 | return NULL; | ||
144 | res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); | ||
145 | if (!res) | ||
146 | return NULL; | ||
147 | res->pid=-1; | ||
148 | asprintf(&res->tty,"/dev/tty%d",vt); | ||
149 | return res; | ||
150 | } | ||
151 | |||
152 | #endif | ||
153 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Comment: | ||
7 | X11 example/test of the libmigdb. | ||
8 | Run it from an X11 terminal (xterm, Eterm, etc.). | ||
9 | |||
10 | ***************************************************************************/ | ||
11 | |||
12 | #include <stdio.h> | ||
13 | #include <unistd.h> //usleep | ||
14 | #include <libesmtp.h> | ||
15 | #include "mi_gdb.h" | ||
16 | |||
17 | void cb_console(const char *str, void *data) | ||
18 | { | ||
19 | printf("CONSOLE> %s\n",str); | ||
20 | } | ||
21 | |||
22 | /* Note that unlike what's documented in gdb docs it isn't usable. */ | ||
23 | void cb_target(const char *str, void *data) | ||
24 | { | ||
25 | printf("TARGET> %s\n",str); | ||
26 | } | ||
27 | |||
28 | void cb_log(const char *str, void *data) | ||
29 | { | ||
30 | printf("LOG> %s\n",str); | ||
31 | } | ||
32 | |||
33 | void cb_to(const char *str, void *data) | ||
34 | { | ||
35 | printf(">> %s",str); | ||
36 | } | ||
37 | |||
38 | void cb_from(const char *str, void *data) | ||
39 | { | ||
40 | printf("<< %s\n",str); | ||
41 | } | ||
42 | |||
43 | volatile int async_c=0; | ||
44 | |||
45 | void cb_async(mi_output *o, void *data) | ||
46 | { | ||
47 | printf("ASYNC\n"); | ||
48 | async_c++; | ||
49 | } | ||
50 | |||
51 | int wait_for_stop(mi_h *h) | ||
52 | { | ||
53 | int res=1; | ||
54 | mi_stop *sr; | ||
55 | mi_frames *f; | ||
56 | |||
57 | while (!mi_get_response(h)) | ||
58 | usleep(1000); | ||
59 | /* The end of the async. */ | ||
60 | sr=mi_res_stop(h); | ||
61 | if (sr) | ||
62 | { | ||
63 | printf("Stopped, reason: %s\n",mi_reason_enum_to_str(sr->reason)); | ||
64 | printf("Received signal name: %s\n", sr->signal_name); | ||
65 | printf("Received signal meaning: %s\n", sr->signal_meaning); | ||
66 | //printf("In file: %s\n", sr->frame->file); | ||
67 | //printf("Line Number: %d\n", sr->frame->line); | ||
68 | f = gmi_stack_info_frame(h); | ||
69 | mi_free_stop(sr); | ||
70 | } | ||
71 | else | ||
72 | { | ||
73 | printf("Error while waiting\n"); | ||
74 | printf("mi_error: %d\nmi_error_from_gdb: %s\n",mi_error,mi_error_from_gdb); | ||
75 | res=0; | ||
76 | } | ||
77 | return res; | ||
78 | } | ||
79 | |||
80 | int main(int argc, char *argv[]) | ||
81 | { | ||
82 | mi_aux_term *xterm_tty=NULL; | ||
83 | |||
84 | /* This is like a file-handle for fopen. | ||
85 | Here we have all the state of gdb "connection". */ | ||
86 | mi_h *h; | ||
87 | |||
88 | /* Connect to gdb child. */ | ||
89 | h=mi_connect_local(); | ||
90 | if (!h) | ||
91 | { | ||
92 | printf("Connect failed\n"); | ||
93 | return 1; | ||
94 | } | ||
95 | printf("Connected to gdb!\n"); | ||
96 | |||
97 | /* Set all callbacks. */ | ||
98 | mi_set_console_cb(h,cb_console,NULL); | ||
99 | mi_set_target_cb(h,cb_target,NULL); | ||
100 | mi_set_log_cb(h,cb_log,NULL); | ||
101 | mi_set_async_cb(h,cb_async,NULL); | ||
102 | mi_set_to_gdb_cb(h,cb_to,NULL); | ||
103 | mi_set_from_gdb_cb(h,cb_from,NULL); | ||
104 | |||
105 | /* Set the name of the child and the command line aguments. */ | ||
106 | if (!gmi_set_exec(h,"bug_null_pointer_exception", NULL)) | ||
107 | { | ||
108 | printf("Error setting exec y args\n"); | ||
109 | mi_disconnect(h); | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | /* Tell gdb to attach the child to a terminal. */ | ||
114 | if (!gmi_target_terminal(h, ttyname(STDIN_FILENO))) | ||
115 | { | ||
116 | printf("Error selecting target terminal\n"); | ||
117 | mi_disconnect(h); | ||
118 | return 1; | ||
119 | } | ||
120 | |||
121 | /* Run the program. */ | ||
122 | if (!gmi_exec_run(h)) | ||
123 | { | ||
124 | printf("Error in run!\n"); | ||
125 | mi_disconnect(h); | ||
126 | return 1; | ||
127 | } | ||
128 | /* Here we should be stopped when the program crashes */ | ||
129 | if (!wait_for_stop(h)) | ||
130 | { | ||
131 | mi_disconnect(h); | ||
132 | return 1; | ||
133 | } | ||
134 | |||
135 | /* Continue execution. */ | ||
136 | if (!gmi_exec_continue(h)) | ||
137 | { | ||
138 | printf("Error in continue!\n"); | ||
139 | mi_disconnect(h); | ||
140 | return 1; | ||
141 | } | ||
142 | /* Here we should be terminated. */ | ||
143 | if (!wait_for_stop(h)) | ||
144 | { | ||
145 | mi_disconnect(h); | ||
146 | return 1; | ||
147 | } | ||
148 | |||
149 | /* Exit from gdb. */ | ||
150 | gmi_gdb_exit(h); | ||
151 | /* Close the connection. */ | ||
152 | mi_disconnect(h); | ||
153 | /* Wait 5 seconds and close the auxiliar terminal. */ | ||
154 | printf("Waiting 5 seconds\n"); | ||
155 | sleep(5); | ||
156 | gmi_end_aux_term(xterm_tty); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
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 @@ | |||
1 | |||
2 | |||
3 | int main(int argc, char *argv[]) | ||
4 | { | ||
5 | return 0; | ||
6 | } \ 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 @@ | |||
1 | #ifndef LIBMIGDB_H_ | ||
2 | #define LIBMIGDB_H_ | ||
3 | |||
4 | #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 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <ctype.h> | ||
4 | #include <unistd.h> | ||
5 | #include <getopt.h> | ||
6 | #include <string.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <signal.h> | ||
9 | #include <errno.h> | ||
10 | #include <stdarg.h> | ||
11 | |||
12 | #include <openssl/ssl.h> | ||
13 | #include <auth-client.h> | ||
14 | #include <libesmtp.h> | ||
15 | |||
16 | #if !defined (__GNUC__) || __GNUC__ < 2 | ||
17 | # define __attribute__(x) | ||
18 | #endif | ||
19 | #define unused __attribute__((unused)) | ||
20 | |||
21 | |||
22 | int | ||
23 | handle_invalid_peer_certificate(long vfy_result) | ||
24 | { | ||
25 | const char *k ="rare error"; | ||
26 | switch(vfy_result) { | ||
27 | case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: | ||
28 | k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"; break; | ||
29 | case X509_V_ERR_UNABLE_TO_GET_CRL: | ||
30 | k="X509_V_ERR_UNABLE_TO_GET_CRL"; break; | ||
31 | case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: | ||
32 | k="X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"; break; | ||
33 | case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: | ||
34 | k="X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"; break; | ||
35 | case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: | ||
36 | k="X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"; break; | ||
37 | case X509_V_ERR_CERT_SIGNATURE_FAILURE: | ||
38 | k="X509_V_ERR_CERT_SIGNATURE_FAILURE"; break; | ||
39 | case X509_V_ERR_CRL_SIGNATURE_FAILURE: | ||
40 | k="X509_V_ERR_CRL_SIGNATURE_FAILURE"; break; | ||
41 | case X509_V_ERR_CERT_NOT_YET_VALID: | ||
42 | k="X509_V_ERR_CERT_NOT_YET_VALID"; break; | ||
43 | case X509_V_ERR_CERT_HAS_EXPIRED: | ||
44 | k="X509_V_ERR_CERT_HAS_EXPIRED"; break; | ||
45 | case X509_V_ERR_CRL_NOT_YET_VALID: | ||
46 | k="X509_V_ERR_CRL_NOT_YET_VALID"; break; | ||
47 | case X509_V_ERR_CRL_HAS_EXPIRED: | ||
48 | k="X509_V_ERR_CRL_HAS_EXPIRED"; break; | ||
49 | case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: | ||
50 | k="X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"; break; | ||
51 | case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: | ||
52 | k="X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"; break; | ||
53 | case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: | ||
54 | k="X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"; break; | ||
55 | case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: | ||
56 | k="X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"; break; | ||
57 | case X509_V_ERR_OUT_OF_MEM: | ||
58 | k="X509_V_ERR_OUT_OF_MEM"; break; | ||
59 | case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: | ||
60 | k="X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"; break; | ||
61 | case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: | ||
62 | k="X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"; break; | ||
63 | case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: | ||
64 | k="X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"; break; | ||
65 | case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: | ||
66 | k="X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"; break; | ||
67 | case X509_V_ERR_CERT_CHAIN_TOO_LONG: | ||
68 | k="X509_V_ERR_CERT_CHAIN_TOO_LONG"; break; | ||
69 | case X509_V_ERR_CERT_REVOKED: | ||
70 | k="X509_V_ERR_CERT_REVOKED"; break; | ||
71 | case X509_V_ERR_INVALID_CA: | ||
72 | k="X509_V_ERR_INVALID_CA"; break; | ||
73 | case X509_V_ERR_PATH_LENGTH_EXCEEDED: | ||
74 | k="X509_V_ERR_PATH_LENGTH_EXCEEDED"; break; | ||
75 | case X509_V_ERR_INVALID_PURPOSE: | ||
76 | k="X509_V_ERR_INVALID_PURPOSE"; break; | ||
77 | case X509_V_ERR_CERT_UNTRUSTED: | ||
78 | k="X509_V_ERR_CERT_UNTRUSTED"; break; | ||
79 | case X509_V_ERR_CERT_REJECTED: | ||
80 | k="X509_V_ERR_CERT_REJECTED"; break; | ||
81 | } | ||
82 | printf("SMTP_EV_INVALID_PEER_CERTIFICATE: %ld: %s\n", vfy_result, k); | ||
83 | return 1; /* Accept the problem */ | ||
84 | } | ||
85 | |||
86 | |||
87 | void event_cb (smtp_session_t session, int event_no, void *arg,...) | ||
88 | { | ||
89 | va_list alist; | ||
90 | int *ok; | ||
91 | |||
92 | va_start(alist, arg); | ||
93 | switch(event_no) { | ||
94 | case SMTP_EV_CONNECT: | ||
95 | case SMTP_EV_MAILSTATUS: | ||
96 | case SMTP_EV_RCPTSTATUS: | ||
97 | case SMTP_EV_MESSAGEDATA: | ||
98 | case SMTP_EV_MESSAGESENT: | ||
99 | case SMTP_EV_DISCONNECT: break; | ||
100 | case SMTP_EV_WEAK_CIPHER: { | ||
101 | int bits; | ||
102 | bits = va_arg(alist, long); ok = va_arg(alist, int*); | ||
103 | printf("SMTP_EV_WEAK_CIPHER, bits=%d - accepted.\n", bits); | ||
104 | *ok = 1; break; | ||
105 | } | ||
106 | case SMTP_EV_STARTTLS_OK: | ||
107 | puts("SMTP_EV_STARTTLS_OK - TLS started here."); break; | ||
108 | case SMTP_EV_INVALID_PEER_CERTIFICATE: { | ||
109 | long vfy_result; | ||
110 | vfy_result = va_arg(alist, long); ok = va_arg(alist, int*); | ||
111 | *ok = handle_invalid_peer_certificate(vfy_result); | ||
112 | break; | ||
113 | } | ||
114 | case SMTP_EV_NO_PEER_CERTIFICATE: { | ||
115 | ok = va_arg(alist, int*); | ||
116 | puts("SMTP_EV_NO_PEER_CERTIFICATE - accepted."); | ||
117 | *ok = 1; break; | ||
118 | } | ||
119 | case SMTP_EV_WRONG_PEER_CERTIFICATE: { | ||
120 | ok = va_arg(alist, int*); | ||
121 | puts("SMTP_EV_WRONG_PEER_CERTIFICATE - accepted."); | ||
122 | *ok = 1; break; | ||
123 | } | ||
124 | case SMTP_EV_NO_CLIENT_CERTIFICATE: { | ||
125 | ok = va_arg(alist, int*); | ||
126 | puts("SMTP_EV_NO_CLIENT_CERTIFICATE - accepted."); | ||
127 | *ok = 1; break; | ||
128 | } | ||
129 | default: | ||
130 | printf("Got event: %d - ignored.\n", event_no); | ||
131 | } | ||
132 | va_end(alist); | ||
133 | } | ||
134 | |||
135 | |||
136 | /* Callback to prnt the recipient status */ | ||
137 | void | ||
138 | print_recipient_status (smtp_recipient_t recipient, | ||
139 | const char *mailbox, void *arg unused) | ||
140 | { | ||
141 | const smtp_status_t *status; | ||
142 | |||
143 | status = smtp_recipient_status (recipient); | ||
144 | printf ("%s: %d %s", mailbox, status->code, status->text); | ||
145 | } | ||
146 | |||
147 | |||
148 | void sendMail() | ||
149 | { | ||
150 | smtp_session_t session; | ||
151 | smtp_message_t message; | ||
152 | smtp_recipient_t recipient; | ||
153 | auth_context_t authctx; | ||
154 | const smtp_status_t *status; | ||
155 | struct sigaction sa; | ||
156 | char *host = "localhost:25"; | ||
157 | char *from = "gnunet-monkey"; | ||
158 | char *subject = "e-mail from Libesmtp!"; | ||
159 | const char *recipient_address = "halims@in.tum.de"; | ||
160 | char tempFileName[1000]; | ||
161 | int tempFd; | ||
162 | FILE *fp; | ||
163 | enum notify_flags notify = Notify_SUCCESS | Notify_FAILURE; | ||
164 | |||
165 | auth_client_init(); | ||
166 | session = smtp_create_session(); | ||
167 | message = smtp_add_message(session); | ||
168 | |||
169 | /* Ignore sigpipe */ | ||
170 | sa.sa_handler = SIG_IGN; | ||
171 | sigemptyset(&sa.sa_mask); | ||
172 | sa.sa_flags = 0; | ||
173 | sigaction(SIGPIPE, &sa, NULL); | ||
174 | |||
175 | |||
176 | smtp_set_server(session, host); | ||
177 | smtp_set_eventcb(session, event_cb, NULL); | ||
178 | |||
179 | /* Set the reverse path for the mail envelope. (NULL is ok) | ||
180 | */ | ||
181 | smtp_set_reverse_path(message, from); | ||
182 | |||
183 | /* Set the Subject: header. For no reason, we want the supplied subject | ||
184 | to override any subject line in the message headers. */ | ||
185 | if (subject != NULL) { | ||
186 | smtp_set_header(message, "Subject", subject); | ||
187 | smtp_set_header_option(message, "Subject", Hdr_OVERRIDE, 1); | ||
188 | } | ||
189 | |||
190 | |||
191 | /* Prepare message */ | ||
192 | memset(tempFileName, 0, sizeof(tempFileName)); | ||
193 | sprintf(tempFileName, "/tmp/messageXXXXXX"); | ||
194 | tempFd = mkstemp(tempFileName); | ||
195 | fp = fdopen(tempFd, "w"); | ||
196 | fprintf(fp, "Hello! This is a test message!\r\n"); | ||
197 | fclose(fp); | ||
198 | fp = fopen(tempFileName, "r"); | ||
199 | smtp_set_message_fp(message, fp); | ||
200 | |||
201 | |||
202 | recipient = smtp_add_recipient(message, recipient_address); | ||
203 | |||
204 | smtp_dsn_set_notify (recipient, notify); | ||
205 | |||
206 | /* Initiate a connection to the SMTP server and transfer the | ||
207 | message. */ | ||
208 | if (!smtp_start_session(session)) { | ||
209 | char buf[128]; | ||
210 | |||
211 | fprintf(stderr, "SMTP server problem %s\n", smtp_strerror(smtp_errno(), | ||
212 | buf, sizeof buf)); | ||
213 | } else { | ||
214 | /* Report on the success or otherwise of the mail transfer. | ||
215 | */ | ||
216 | status = smtp_message_transfer_status(message); | ||
217 | printf("%d %s", status->code, (status->text != NULL) ? status->text | ||
218 | : "\n"); | ||
219 | smtp_enumerate_recipients(message, print_recipient_status, NULL); | ||
220 | } | ||
221 | |||
222 | /* Free resources consumed by the program. | ||
223 | */ | ||
224 | smtp_destroy_session(session); | ||
225 | auth_destroy_context(authctx); | ||
226 | fclose(fp); | ||
227 | auth_client_exit(); | ||
228 | exit(0); | ||
229 | } | ||
230 | |||
231 | int main() | ||
232 | { | ||
233 | sendMail(); | ||
234 | return 0; | ||
235 | } | ||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2009 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Comments: | ||
7 | Main header for libmigdb. | ||
8 | |||
9 | ***************************************************************************/ | ||
10 | |||
11 | #ifdef __cplusplus | ||
12 | extern "C" { | ||
13 | #endif | ||
14 | |||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <unistd.h> /* pid_t */ | ||
18 | |||
19 | #define MI_OK 0 | ||
20 | #define MI_OUT_OF_MEMORY 1 | ||
21 | #define MI_PIPE_CREATE 2 | ||
22 | #define MI_FORK 3 | ||
23 | #define MI_DEBUGGER_RUN 4 | ||
24 | #define MI_PARSER 5 | ||
25 | #define MI_UNKNOWN_ASYNC 6 | ||
26 | #define MI_UNKNOWN_RESULT 7 | ||
27 | #define MI_FROM_GDB 8 | ||
28 | #define MI_GDB_TIME_OUT 9 | ||
29 | #define MI_GDB_DIED 10 | ||
30 | #define MI_MISSING_XTERM 11 | ||
31 | #define MI_CREATE_TEMPORAL 12 | ||
32 | #define MI_MISSING_GDB 13 | ||
33 | #define MI_LAST_ERROR 13 | ||
34 | |||
35 | #define MI_R_NONE 0 /* We are no waiting any response. */ | ||
36 | #define MI_R_SKIP 1 /* We want to discard it. */ | ||
37 | #define MI_R_FE_AND_S 2 /* Wait for done. */ | ||
38 | #define MI_R_E_ARGS 3 | ||
39 | |||
40 | enum mi_val_type { t_const, t_tuple, t_list }; | ||
41 | |||
42 | /* Types and subtypes. */ | ||
43 | /* Type. */ | ||
44 | #define MI_T_OUT_OF_BAND 0 | ||
45 | #define MI_T_RESULT_RECORD 1 | ||
46 | /* Out of band subtypes. */ | ||
47 | #define MI_ST_ASYNC 0 | ||
48 | #define MI_ST_STREAM 1 | ||
49 | /* Async sub-subtypes. */ | ||
50 | #define MI_SST_EXEC 0 | ||
51 | #define MI_SST_STATUS 1 | ||
52 | #define MI_SST_NOTIFY 2 | ||
53 | /* Stream sub-subtypes. */ | ||
54 | #define MI_SST_CONSOLE 3 | ||
55 | #define MI_SST_TARGET 4 | ||
56 | #define MI_SST_LOG 5 | ||
57 | /* Classes. */ | ||
58 | /* Async classes. */ | ||
59 | #define MI_CL_UNKNOWN 0 | ||
60 | #define MI_CL_STOPPED 1 | ||
61 | #define MI_CL_DOWNLOAD 2 | ||
62 | /* Result classes. */ | ||
63 | #define MI_CL_DONE 2 | ||
64 | #define MI_CL_RUNNING 3 | ||
65 | #define MI_CL_CONNECTED 4 | ||
66 | #define MI_CL_ERROR 5 | ||
67 | #define MI_CL_EXIT 6 | ||
68 | |||
69 | #define MI_DEFAULT_TIME_OUT 10 | ||
70 | |||
71 | #define MI_DIS_ASM 0 | ||
72 | #define MI_DIS_SRC_ASM 1 | ||
73 | |||
74 | /* Implemented workaround for gdb bugs that we can dis/enable. */ | ||
75 | /* At least gdb<=6.1.1 fails to find a source file with absolute path if the | ||
76 | name is for a psym instead of a sym. psym==partially loaded symbol table. */ | ||
77 | #define MI_PSYM_SEARCH 0 | ||
78 | |||
79 | #define MI_VERSION_STR "0.8.12" | ||
80 | #define MI_VERSION_MAJOR 0 | ||
81 | #define MI_VERSION_MIDDLE 8 | ||
82 | #define MI_VERSION_MINOR 12 | ||
83 | |||
84 | struct mi_results_struct | ||
85 | { | ||
86 | char *var; /* Result name or NULL if just a value. */ | ||
87 | enum mi_val_type type; | ||
88 | union | ||
89 | { | ||
90 | char *cstr; | ||
91 | struct mi_results_struct *rs; | ||
92 | } v; | ||
93 | struct mi_results_struct *next; | ||
94 | }; | ||
95 | typedef struct mi_results_struct mi_results; | ||
96 | |||
97 | struct mi_output_struct | ||
98 | { | ||
99 | /* Type of output. */ | ||
100 | char type; | ||
101 | char stype; | ||
102 | char sstype; | ||
103 | char tclass; | ||
104 | /* Content. */ | ||
105 | mi_results *c; | ||
106 | /* Always modeled as a list. */ | ||
107 | struct mi_output_struct *next; | ||
108 | }; | ||
109 | typedef struct mi_output_struct mi_output; | ||
110 | |||
111 | typedef void (*stream_cb)(const char *, void *); | ||
112 | typedef void (*async_cb)(mi_output *o, void *); | ||
113 | typedef int (*tm_cb)(void *); | ||
114 | |||
115 | /* Values of this structure shouldn't be manipulated by the user. */ | ||
116 | struct mi_h_struct | ||
117 | { | ||
118 | /* Pipes connected to gdb. */ | ||
119 | int to_gdb[2]; | ||
120 | int from_gdb[2]; | ||
121 | /* Streams for the pipes. */ | ||
122 | FILE *to, *from; | ||
123 | /* PID of child gdb. */ | ||
124 | pid_t pid; | ||
125 | char died; | ||
126 | /* Which rensponse we are waiting for. */ | ||
127 | /*int response;*/ | ||
128 | /* The line we are reading. */ | ||
129 | char *line; | ||
130 | int llen, lread; | ||
131 | /* Parsed output. */ | ||
132 | mi_output *po, *last; | ||
133 | /* Tunneled streams callbacks. */ | ||
134 | stream_cb console; | ||
135 | void *console_data; | ||
136 | stream_cb target; | ||
137 | void *target_data; | ||
138 | stream_cb log; | ||
139 | void *log_data; | ||
140 | /* Async responses callback. */ | ||
141 | async_cb async; | ||
142 | void *async_data; | ||
143 | /* Callbacks to get echo of gdb dialog. */ | ||
144 | stream_cb to_gdb_echo; | ||
145 | void *to_gdb_echo_data; | ||
146 | stream_cb from_gdb_echo; | ||
147 | void *from_gdb_echo_data; | ||
148 | /* Time out */ | ||
149 | tm_cb time_out_cb; | ||
150 | void *time_out_cb_data; | ||
151 | int time_out; | ||
152 | /* Ugly workaround for some of the show responses :-( */ | ||
153 | int catch_console; | ||
154 | char *catched_console; | ||
155 | /* MI version, currently unknown but the user can force v2 */ | ||
156 | unsigned version; | ||
157 | }; | ||
158 | typedef struct mi_h_struct mi_h; | ||
159 | |||
160 | #define MI_TO(a) ((a)->to_gdb[1]) | ||
161 | |||
162 | enum mi_bkp_type { t_unknown=0, t_breakpoint=1, t_hw=2 }; | ||
163 | enum mi_bkp_disp { d_unknown=0, d_keep=1, d_del=2 }; | ||
164 | enum mi_bkp_mode { m_file_line=0, m_function=1, m_file_function=2, m_address=3 }; | ||
165 | |||
166 | struct mi_bkpt_struct | ||
167 | { | ||
168 | int number; | ||
169 | enum mi_bkp_type type; | ||
170 | enum mi_bkp_disp disp; /* keep or del if temporal */ | ||
171 | char enabled; | ||
172 | void *addr; | ||
173 | char *func; | ||
174 | char *file; | ||
175 | int line; | ||
176 | int ignore; | ||
177 | int times; | ||
178 | |||
179 | /* For the user: */ | ||
180 | char *cond; | ||
181 | char *file_abs; | ||
182 | int thread; | ||
183 | enum mi_bkp_mode mode; | ||
184 | struct mi_bkpt_struct *next; | ||
185 | }; | ||
186 | typedef struct mi_bkpt_struct mi_bkpt; | ||
187 | |||
188 | enum mi_wp_mode { wm_unknown=0, wm_write=1, wm_read=2, wm_rw=3 }; | ||
189 | |||
190 | struct mi_wp_struct | ||
191 | { | ||
192 | int number; | ||
193 | char *exp; | ||
194 | enum mi_wp_mode mode; | ||
195 | |||
196 | /* For the user: */ | ||
197 | struct mi_wp_struct *next; | ||
198 | char enabled; | ||
199 | }; | ||
200 | typedef struct mi_wp_struct mi_wp; | ||
201 | |||
202 | struct mi_frames_struct | ||
203 | { | ||
204 | int level; /* The frame number, 0 being the topmost frame, i.e. the innermost | ||
205 | function. */ | ||
206 | void *addr; /* The `$pc' value for that frame. */ | ||
207 | char *func; /* Function name. */ | ||
208 | char *file; /* File name of the source file where the function lives. */ | ||
209 | char *from; | ||
210 | int line; /* Line number corresponding to the `$pc'. */ | ||
211 | /* When arguments are available: */ | ||
212 | mi_results *args; | ||
213 | int thread_id; | ||
214 | /* When more than one is provided: */ | ||
215 | struct mi_frames_struct *next; | ||
216 | }; | ||
217 | typedef struct mi_frames_struct mi_frames; | ||
218 | |||
219 | struct mi_aux_term_struct | ||
220 | { | ||
221 | pid_t pid; | ||
222 | char *tty; | ||
223 | }; | ||
224 | typedef struct mi_aux_term_struct mi_aux_term; | ||
225 | |||
226 | struct mi_pty_struct | ||
227 | { | ||
228 | char *slave; | ||
229 | int master; | ||
230 | }; | ||
231 | typedef struct mi_pty_struct mi_pty; | ||
232 | |||
233 | enum mi_gvar_fmt { fm_natural=0, fm_binary=1, fm_decimal=2, fm_hexadecimal=3, | ||
234 | fm_octal=4, | ||
235 | /* Only for registers format: */ | ||
236 | fm_raw=5 }; | ||
237 | enum mi_gvar_lang { lg_unknown=0, lg_c, lg_cpp, lg_java }; | ||
238 | |||
239 | #define MI_ATTR_DONT_KNOW 0 | ||
240 | #define MI_ATTR_NONEDITABLE 1 | ||
241 | #define MI_ATTR_EDITABLE 2 | ||
242 | |||
243 | struct mi_gvar_struct | ||
244 | { | ||
245 | char *name; | ||
246 | int numchild; | ||
247 | char *type; | ||
248 | enum mi_gvar_fmt format; | ||
249 | enum mi_gvar_lang lang; | ||
250 | char *exp; | ||
251 | int attr; | ||
252 | |||
253 | /* MI v2 fills it, not yet implemented here. */ | ||
254 | /* Use gmi_var_evaluate_expression. */ | ||
255 | char *value; | ||
256 | |||
257 | /* Pointer to the parent. NULL if none. */ | ||
258 | struct mi_gvar_struct *parent; | ||
259 | /* List containing the children. | ||
260 | Filled by gmi_var_list_children. | ||
261 | NULL if numchild==0 or not yet filled. */ | ||
262 | struct mi_gvar_struct *child; | ||
263 | /* Next var in the list. */ | ||
264 | struct mi_gvar_struct *next; | ||
265 | |||
266 | /* For the user: */ | ||
267 | char opened; /* We will show its children. 1 when we fill "child" */ | ||
268 | char changed; /* Needs to be updated. 0 when created. */ | ||
269 | int vischild; /* How many items visible. numchild when we fill "child" */ | ||
270 | int depth; /* How deep is this var. */ | ||
271 | char ispointer; | ||
272 | }; | ||
273 | typedef struct mi_gvar_struct mi_gvar; | ||
274 | |||
275 | struct mi_gvar_chg_struct | ||
276 | { | ||
277 | char *name; | ||
278 | int in_scope; /* if true the other fields apply. */ | ||
279 | char *new_type; /* NULL if type_changed==false */ | ||
280 | int new_num_children; /* only when new_type!=NULL */ | ||
281 | |||
282 | struct mi_gvar_chg_struct *next; | ||
283 | }; | ||
284 | typedef struct mi_gvar_chg_struct mi_gvar_chg; | ||
285 | |||
286 | |||
287 | /* A list of assembler instructions. */ | ||
288 | struct mi_asm_insn_struct | ||
289 | { | ||
290 | void *addr; | ||
291 | char *func; | ||
292 | unsigned offset; | ||
293 | char *inst; | ||
294 | |||
295 | struct mi_asm_insn_struct *next; | ||
296 | }; | ||
297 | typedef struct mi_asm_insn_struct mi_asm_insn; | ||
298 | |||
299 | /* A list of source lines containing assembler instructions. */ | ||
300 | struct mi_asm_insns_struct | ||
301 | { | ||
302 | char *file; | ||
303 | int line; | ||
304 | mi_asm_insn *ins; | ||
305 | |||
306 | struct mi_asm_insns_struct *next; | ||
307 | }; | ||
308 | typedef struct mi_asm_insns_struct mi_asm_insns; | ||
309 | |||
310 | /* Changed register. */ | ||
311 | struct mi_chg_reg_struct | ||
312 | { | ||
313 | int reg; | ||
314 | char *val; | ||
315 | char *name; | ||
316 | char updated; | ||
317 | |||
318 | struct mi_chg_reg_struct *next; | ||
319 | }; | ||
320 | typedef struct mi_chg_reg_struct mi_chg_reg; | ||
321 | |||
322 | /* | ||
323 | Examining gdb sources and looking at docs I can see the following "stop" | ||
324 | reasons: | ||
325 | |||
326 | Breakpoints: | ||
327 | a) breakpoint-hit (bkptno) + frame | ||
328 | Also: without reason for temporal breakpoints. | ||
329 | |||
330 | Watchpoints: | ||
331 | b) watchpoint-trigger (wpt={number,exp};value={old,new}) + frame | ||
332 | c) read-watchpoint-trigger (hw-rwpt{number,exp};value={value}) + frame | ||
333 | d) access-watchpoint-trigger (hw-awpt{number,exp};value={[old,]new}) + frame | ||
334 | e) watchpoint-scope (wpnum) + frame | ||
335 | |||
336 | Movement: | ||
337 | f) function-finished ([gdb-result-var,return-value]) + frame | ||
338 | g) location-reached + frame | ||
339 | h) end-stepping-range + frame | ||
340 | |||
341 | Exit: | ||
342 | i) exited-signalled (signal-name,signal-meaning) | ||
343 | j) exited (exit-code) | ||
344 | k) exited-normally | ||
345 | |||
346 | Signal: | ||
347 | l) signal-received (signal-name,signal-meaning) + frame | ||
348 | |||
349 | Plus: thread-id | ||
350 | */ | ||
351 | enum mi_stop_reason | ||
352 | { | ||
353 | sr_unknown=0, | ||
354 | sr_bkpt_hit, | ||
355 | sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, | ||
356 | sr_function_finished, sr_location_reached, sr_end_stepping_range, | ||
357 | sr_exited_signalled, sr_exited, sr_exited_normally, | ||
358 | sr_signal_received | ||
359 | }; | ||
360 | |||
361 | struct mi_stop_struct | ||
362 | { | ||
363 | enum mi_stop_reason reason; /* If more than one reason just the last. */ | ||
364 | /* Flags indicating if non-pointer fields are filled. */ | ||
365 | char have_thread_id; | ||
366 | char have_bkptno; | ||
367 | char have_exit_code; | ||
368 | char have_wpno; | ||
369 | /* Where stopped. Doesn't exist for sr_exited*. */ | ||
370 | int thread_id; | ||
371 | mi_frames *frame; | ||
372 | /* sr_bkpt_hit */ | ||
373 | int bkptno; | ||
374 | /* sr_*wp_* no scope */ | ||
375 | mi_wp *wp; | ||
376 | char *wp_old; | ||
377 | char *wp_val; | ||
378 | /* sr_wp_scope */ | ||
379 | int wpno; | ||
380 | /* sr_function_finished. Not for void func. */ | ||
381 | char *gdb_result_var; | ||
382 | char *return_value; | ||
383 | /* sr_exited_signalled, sr_signal_received */ | ||
384 | char *signal_name; | ||
385 | char *signal_meaning; | ||
386 | /* sr_exited */ | ||
387 | int exit_code; | ||
388 | }; | ||
389 | typedef struct mi_stop_struct mi_stop; | ||
390 | |||
391 | /* Variable containing the last error. */ | ||
392 | extern int mi_error; | ||
393 | extern char *mi_error_from_gdb; | ||
394 | const char *mi_get_error_str(); | ||
395 | |||
396 | /* Indicate the name of gdb exe. Default is /usr/bin/gdb */ | ||
397 | void mi_set_gdb_exe(const char *name); | ||
398 | const char *mi_get_gdb_exe(); | ||
399 | /* Indicate the name of a file containing commands to send at start-up */ | ||
400 | void mi_set_gdb_start(const char *name); | ||
401 | const char *mi_get_gdb_start(); | ||
402 | /* Indicate the name of a file containing commands to send after connection */ | ||
403 | void mi_set_gdb_conn(const char *name); | ||
404 | const char *mi_get_gdb_conn(); | ||
405 | void mi_send_target_commands(mi_h *h); | ||
406 | /* Connect to a local copy of gdb. */ | ||
407 | mi_h *mi_connect_local(); | ||
408 | /* Close connection. You should ask gdb to quit first. */ | ||
409 | void mi_disconnect(mi_h *h); | ||
410 | /* Force MI version. */ | ||
411 | #define MI_VERSION2U(maj,mid,min) (maj*0x1000000+mid*0x10000+min) | ||
412 | void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, | ||
413 | unsigned vMinor); | ||
414 | void mi_set_workaround(unsigned wa, int enable); | ||
415 | int mi_get_workaround(unsigned wa); | ||
416 | /* Parse gdb output. */ | ||
417 | mi_output *mi_parse_gdb_output(const char *str); | ||
418 | /* Functions to set/get the tunneled streams callbacks. */ | ||
419 | void mi_set_console_cb(mi_h *h, stream_cb cb, void *data); | ||
420 | void mi_set_target_cb(mi_h *h, stream_cb cb, void *data); | ||
421 | void mi_set_log_cb(mi_h *h, stream_cb cb, void *data); | ||
422 | stream_cb mi_get_console_cb(mi_h *h, void **data); | ||
423 | stream_cb mi_get_target_cb(mi_h *h, void **data); | ||
424 | stream_cb mi_get_log_cb(mi_h *h, void **data); | ||
425 | /* The callback to deal with async events. */ | ||
426 | void mi_set_async_cb(mi_h *h, async_cb cb, void *data); | ||
427 | async_cb mi_get_async_cb(mi_h *h, void **data); | ||
428 | /* Time out in gdb responses. */ | ||
429 | void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data); | ||
430 | tm_cb mi_get_time_out_cb(mi_h *h, void **data); | ||
431 | void mi_set_time_out(mi_h *h, int to); | ||
432 | int mi_get_time_out(mi_h *h); | ||
433 | /* Callbacks to "see" the dialog with gdb. */ | ||
434 | void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data); | ||
435 | void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data); | ||
436 | stream_cb mi_get_to_gdb_cb(mi_h *h, void **data); | ||
437 | stream_cb mi_get_from_gdb_cb(mi_h *h, void **data); | ||
438 | /* Sends a message to gdb. */ | ||
439 | int mi_send(mi_h *h, const char *format, ...); | ||
440 | /* Wait until gdb sends a response. */ | ||
441 | mi_output *mi_get_response_blk(mi_h *h); | ||
442 | /* Check if gdb sent a complete response. Use with mi_retire_response. */ | ||
443 | int mi_get_response(mi_h *h); | ||
444 | /* Get the last response. Use with mi_get_response. */ | ||
445 | mi_output *mi_retire_response(mi_h *h); | ||
446 | /* Look for a result record in gdb output. */ | ||
447 | mi_output *mi_get_rrecord(mi_output *r); | ||
448 | /* Look if the output contains an async stop. | ||
449 | If that's the case return the reason for the stop. | ||
450 | If the output contains an error the description is returned in reason. */ | ||
451 | int mi_get_async_stop_reason(mi_output *r, char **reason); | ||
452 | mi_stop *mi_get_stopped(mi_results *r); | ||
453 | mi_frames *mi_get_async_frame(mi_output *r); | ||
454 | /* Wait until gdb sends a response. | ||
455 | Then check if the response is of the desired type. */ | ||
456 | int mi_res_simple_exit(mi_h *h); | ||
457 | int mi_res_simple_done(mi_h *h); | ||
458 | int mi_res_simple_running(mi_h *h); | ||
459 | int mi_res_simple_connected(mi_h *h); | ||
460 | /* It additionally extracts an specified variable. */ | ||
461 | mi_results *mi_res_done_var(mi_h *h, const char *var); | ||
462 | /* Extract a frames list from the response. */ | ||
463 | mi_frames *mi_res_frames_array(mi_h *h, const char *var); | ||
464 | mi_frames *mi_res_frames_list(mi_h *h); | ||
465 | mi_frames *mi_parse_frame(mi_results *c); | ||
466 | mi_frames *mi_res_frame(mi_h *h); | ||
467 | /* Create an auxiliar terminal using xterm. */ | ||
468 | mi_aux_term *gmi_start_xterm(); | ||
469 | /* Indicate the name of xterm exe. Default is /usr/bin/X11/xterm */ | ||
470 | void mi_set_xterm_exe(const char *name); | ||
471 | const char *mi_get_xterm_exe(); | ||
472 | /* Kill the auxiliar terminal and release the structure. */ | ||
473 | void gmi_end_aux_term(mi_aux_term *t); | ||
474 | /* Look for a free Linux VT for the child. */ | ||
475 | mi_aux_term *gmi_look_for_free_vt(); | ||
476 | /* Look for a free and usable Linux VT. */ | ||
477 | int mi_look_for_free_vt(); | ||
478 | /* Close master and release the structure. */ | ||
479 | void gmi_end_pty(mi_pty *p); | ||
480 | /* Look for a free pseudo terminal. */ | ||
481 | mi_pty *gmi_look_for_free_pty(); | ||
482 | /* Extract a list of thread IDs from response. */ | ||
483 | int mi_res_thread_ids(mi_h *h, int **list); | ||
484 | int mi_get_thread_ids(mi_output *res, int **list); | ||
485 | /* A variable response. */ | ||
486 | mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression); | ||
487 | enum mi_gvar_fmt mi_format_str_to_enum(const char *format); | ||
488 | const char *mi_format_enum_to_str(enum mi_gvar_fmt format); | ||
489 | char mi_format_enum_to_char(enum mi_gvar_fmt format); | ||
490 | enum mi_gvar_lang mi_lang_str_to_enum(const char *lang); | ||
491 | const char *mi_lang_enum_to_str(enum mi_gvar_lang lang); | ||
492 | int mi_res_changelist(mi_h *h, mi_gvar_chg **changed); | ||
493 | int mi_res_children(mi_h *h, mi_gvar *v); | ||
494 | mi_bkpt *mi_res_bkpt(mi_h *h); | ||
495 | mi_wp *mi_res_wp(mi_h *h); | ||
496 | char *mi_res_value(mi_h *h); | ||
497 | mi_stop *mi_res_stop(mi_h *h); | ||
498 | enum mi_stop_reason mi_reason_str_to_enum(const char *s); | ||
499 | const char *mi_reason_enum_to_str(enum mi_stop_reason r); | ||
500 | int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, | ||
501 | unsigned long *addr); | ||
502 | mi_asm_insns *mi_get_asm_insns(mi_h *h); | ||
503 | /* Starting point of the program. */ | ||
504 | void mi_set_main_func(const char *name); | ||
505 | const char *mi_get_main_func(); | ||
506 | mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many); | ||
507 | int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l); | ||
508 | mi_chg_reg *mi_get_list_changed_regs(mi_h *h); | ||
509 | int mi_get_reg_values(mi_h *h, mi_chg_reg *l); | ||
510 | mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many); | ||
511 | int gmi_target_download(mi_h *h); | ||
512 | |||
513 | /* Allocation functions: */ | ||
514 | void *mi_calloc(size_t count, size_t sz); | ||
515 | void *mi_calloc1(size_t sz); | ||
516 | char *mi_malloc(size_t sz); | ||
517 | mi_results *mi_alloc_results(void); | ||
518 | mi_output *mi_alloc_output(void); | ||
519 | mi_frames *mi_alloc_frames(void); | ||
520 | mi_gvar *mi_alloc_gvar(void); | ||
521 | mi_gvar_chg *mi_alloc_gvar_chg(void); | ||
522 | mi_bkpt *mi_alloc_bkpt(void); | ||
523 | mi_wp *mi_alloc_wp(void); | ||
524 | mi_stop *mi_alloc_stop(void); | ||
525 | mi_asm_insns *mi_alloc_asm_insns(void); | ||
526 | mi_asm_insn *mi_alloc_asm_insn(void); | ||
527 | mi_chg_reg *mi_alloc_chg_reg(void); | ||
528 | void mi_free_output(mi_output *r); | ||
529 | void mi_free_output_but(mi_output *r, mi_output *no, mi_results *no_r); | ||
530 | void mi_free_frames(mi_frames *f); | ||
531 | void mi_free_aux_term(mi_aux_term *t); | ||
532 | void mi_free_results(mi_results *r); | ||
533 | void mi_free_results_but(mi_results *r, mi_results *no); | ||
534 | void mi_free_gvar(mi_gvar *v); | ||
535 | void mi_free_gvar_chg(mi_gvar_chg *p); | ||
536 | void mi_free_wp(mi_wp *wp); | ||
537 | void mi_free_stop(mi_stop *s); | ||
538 | void mi_free_asm_insns(mi_asm_insns *i); | ||
539 | void mi_free_asm_insn(mi_asm_insn *i); | ||
540 | void mi_free_charp_list(char **l); | ||
541 | void mi_free_chg_reg(mi_chg_reg *r); | ||
542 | |||
543 | /* Porgram control: */ | ||
544 | /* Specify the executable and arguments for local debug. */ | ||
545 | int gmi_set_exec(mi_h *h, const char *file, const char *args); | ||
546 | /* Start running the executable. Remote sessions starts running. */ | ||
547 | int gmi_exec_run(mi_h *h); | ||
548 | /* Continue the execution after a "stop". */ | ||
549 | int gmi_exec_continue(mi_h *h); | ||
550 | /* Indicate which terminal will use the target program. For local sessions. */ | ||
551 | int gmi_target_terminal(mi_h *h, const char *tty_name); | ||
552 | /* Specify what's the local copy that have debug info. For remote sessions. */ | ||
553 | int gmi_file_symbol_file(mi_h *h, const char *file); | ||
554 | /* Continue until function return, the return value is included in the async | ||
555 | response. */ | ||
556 | int gmi_exec_finish(mi_h *h); | ||
557 | /* Stop the program using SIGINT. */ | ||
558 | int gmi_exec_interrupt(mi_h *h); | ||
559 | /* Next line of code. */ | ||
560 | int gmi_exec_next(mi_h *h); | ||
561 | /* Next count lines of code. */ | ||
562 | int gmi_exec_next_cnt(mi_h *h, int count); | ||
563 | /* Next line of assembler code. */ | ||
564 | int gmi_exec_next_instruction(mi_h *h); | ||
565 | /* Next line of code. Get inside functions. */ | ||
566 | int gmi_exec_step(mi_h *h); | ||
567 | /* Next count lines of code. Get inside functions. */ | ||
568 | int gmi_exec_step_cnt(mi_h *h, int count); | ||
569 | /* Next line of assembler code. Get inside calls. */ | ||
570 | int gmi_exec_step_instruction(mi_h *h); | ||
571 | /* Execute until location is reached. If file is NULL then is until next line. */ | ||
572 | int gmi_exec_until(mi_h *h, const char *file, int line); | ||
573 | int gmi_exec_until_addr(mi_h *h, void *addr); | ||
574 | /* Return to previous frame inmediatly. */ | ||
575 | mi_frames *gmi_exec_return(mi_h *h); | ||
576 | /* Just kill the program. Please read the notes in prg_control.c. */ | ||
577 | int gmi_exec_kill(mi_h *h); | ||
578 | |||
579 | /* Target manipulation: */ | ||
580 | /* Connect to a remote gdbserver using the specified methode. */ | ||
581 | int gmi_target_select(mi_h *h, const char *type, const char *params); | ||
582 | /* Attach to an already running process. */ | ||
583 | mi_frames *gmi_target_attach(mi_h *h, pid_t pid); | ||
584 | /* Detach from an attached process. */ | ||
585 | int gmi_target_detach(mi_h *h); | ||
586 | |||
587 | /* Miscellaneous commands: */ | ||
588 | /* Exit gdb killing the child is it is running. */ | ||
589 | void gmi_gdb_exit(mi_h *h); | ||
590 | /* Send the version to the console. */ | ||
591 | int gmi_gdb_version(mi_h *h); | ||
592 | /* Set a gdb variable. */ | ||
593 | int gmi_gdb_set(mi_h *h, const char *var, const char *val); | ||
594 | /* Get a gdb variable. */ | ||
595 | char *gmi_gdb_show(mi_h *h, const char *var); | ||
596 | |||
597 | /* Breakpoints manipulation: */ | ||
598 | /* Insert a breakpoint at file:line. */ | ||
599 | mi_bkpt *gmi_break_insert(mi_h *h, const char *file, int line); | ||
600 | /* Insert a breakpoint, all available options. */ | ||
601 | mi_bkpt *gmi_break_insert_full(mi_h *h, int temporary, int hard_assist, | ||
602 | const char *cond, int count, int thread, | ||
603 | const char *where); | ||
604 | mi_bkpt *gmi_break_insert_full_fl(mi_h *h, const char *file, int line, | ||
605 | int temporary, int hard_assist, | ||
606 | const char *cond, int count, int thread); | ||
607 | /* Remove a breakpoint. */ | ||
608 | int gmi_break_delete(mi_h *h, int number); | ||
609 | /* Free the memory used for a breakpoint description. */ | ||
610 | void mi_free_bkpt(mi_bkpt *b); | ||
611 | /* Modify the "ignore" count for a breakpoint. */ | ||
612 | int gmi_break_set_times(mi_h *h, int number, int count); | ||
613 | /* Associate a condition with the breakpoint. */ | ||
614 | int gmi_break_set_condition(mi_h *h, int number, const char *condition); | ||
615 | /* Enable or disable a breakpoint. */ | ||
616 | int gmi_break_state(mi_h *h, int number, int enable); | ||
617 | /* Set a watchpoint. It doesn't work for remote targets! */ | ||
618 | mi_wp *gmi_break_watch(mi_h *h, enum mi_wp_mode mode, const char *exp); | ||
619 | |||
620 | /* Data Manipulation. */ | ||
621 | /* Evaluate an expression. Returns a parsed tree. */ | ||
622 | char *gmi_data_evaluate_expression(mi_h *h, const char *expression); | ||
623 | /* Path for sources. */ | ||
624 | int gmi_dir(mi_h *h, const char *path); | ||
625 | /* A very limited "data read memory" implementation. */ | ||
626 | int gmi_read_memory(mi_h *h, const char *exp, unsigned size, | ||
627 | unsigned char *dest, int *na, int convAddr, | ||
628 | unsigned long *addr); | ||
629 | mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, | ||
630 | const char *end, int mode); | ||
631 | mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, | ||
632 | int lines, int mode); | ||
633 | mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many); | ||
634 | int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l); | ||
635 | mi_chg_reg *gmi_data_list_changed_registers(mi_h *h); | ||
636 | int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l); | ||
637 | mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many); | ||
638 | |||
639 | /* Stack manipulation. */ | ||
640 | /* List of frames. Arguments aren't filled. */ | ||
641 | mi_frames *gmi_stack_list_frames(mi_h *h); | ||
642 | /* List of frames. Indicating a range. */ | ||
643 | mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to); | ||
644 | /* List arguments. Only level and args filled. */ | ||
645 | mi_frames *gmi_stack_list_arguments(mi_h *h, int show); | ||
646 | /* List arguments. Indicating a range. Only level and args filled. */ | ||
647 | mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to); | ||
648 | /* Information about the current frame, including args. */ | ||
649 | mi_frames *gmi_stack_info_frame(mi_h *h); | ||
650 | /* Stack info depth. error => -1 */ | ||
651 | int gmi_stack_info_depth_get(mi_h *h); | ||
652 | /* Set stack info depth. error => -1 */ | ||
653 | int gmi_stack_info_depth(mi_h *h, int max_depth); | ||
654 | /* Change current frame. */ | ||
655 | int gmi_stack_select_frame(mi_h *h, int framenum); | ||
656 | /* List of local vars. */ | ||
657 | mi_results *gmi_stack_list_locals(mi_h *h, int show); | ||
658 | |||
659 | /* Thread. */ | ||
660 | /* List available thread ids. */ | ||
661 | int gmi_thread_list_ids(mi_h *h, int **list); | ||
662 | /* Select a thread. */ | ||
663 | mi_frames *gmi_thread_select(mi_h *h, int id); | ||
664 | /* List available threads. */ | ||
665 | mi_frames *gmi_thread_list_all_threads(mi_h *h); | ||
666 | |||
667 | /* Variable objects. */ | ||
668 | /* Create a variable object. */ | ||
669 | mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp); | ||
670 | mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp); | ||
671 | /* Create the variable and also fill the lang and attr fields. */ | ||
672 | mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp); | ||
673 | /* Delete a variable object. Doesn't free the mi_gvar data. */ | ||
674 | int gmi_var_delete(mi_h *h, mi_gvar *var); | ||
675 | /* Set the format used to represent the result. */ | ||
676 | int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format); | ||
677 | /* Fill the format field with info from gdb. */ | ||
678 | int gmi_var_show_format(mi_h *h, mi_gvar *var); | ||
679 | /* Fill the numchild field with info from gdb. */ | ||
680 | int gmi_var_info_num_children(mi_h *h, mi_gvar *var); | ||
681 | /* Fill the type field with info from gdb. */ | ||
682 | int gmi_var_info_type(mi_h *h, mi_gvar *var); | ||
683 | /* Fill the expression and lang fields with info from gdb. | ||
684 | Note that lang isn't filled during creation. */ | ||
685 | int gmi_var_info_expression(mi_h *h, mi_gvar *var); | ||
686 | /* Fill the attr field with info from gdb. | ||
687 | Note that attr isn't filled during creation. */ | ||
688 | int gmi_var_show_attributes(mi_h *h, mi_gvar *var); | ||
689 | /* Update variable. Use NULL for all. | ||
690 | Note that *changed can be NULL if none updated. */ | ||
691 | int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed); | ||
692 | /* Change variable. Fills the value field. */ | ||
693 | int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression); | ||
694 | /* Get current value for a variable. */ | ||
695 | int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var); | ||
696 | /* List children. It ONLY returns the first level information. :-( */ | ||
697 | int gmi_var_list_children(mi_h *h, mi_gvar *var); | ||
698 | |||
699 | #ifdef __cplusplus | ||
700 | }; | ||
701 | |||
702 | /* C++ interface */ | ||
703 | |||
704 | /* | ||
705 | State Can: | ||
706 | disconnected Connect | ||
707 | connected SelectTarget, Disconnect | ||
708 | target_specified TargetUnselect, Run, Set breakpoints/watchpoints, etc. | ||
709 | running Stop | ||
710 | stopped Kill, Restart?, Step, Trace, Continue, etc. | ||
711 | [auto exit] | ||
712 | |||
713 | Modes: | ||
714 | dmX11 Local debug for X11. | ||
715 | dmLinux Local debug for Linux console. | ||
716 | dmRemote Remote debug. | ||
717 | */ | ||
718 | class MIDebugger | ||
719 | { | ||
720 | public: | ||
721 | MIDebugger(); | ||
722 | ~MIDebugger(); | ||
723 | |||
724 | enum eState { disconnected, connected, target_specified, running, stopped }; | ||
725 | enum dMode { dmX11, dmLinux, dmRemote, dmPID }; | ||
726 | enum endianType { enUnknown, enLittle, enBig }; | ||
727 | // Currently tested architectures | ||
728 | enum archType { arUnknown, arIA32, arSPARC, arPIC14, arAVR, arUnsupported }; | ||
729 | |||
730 | int Connect(bool remote=false); /* remote is currently ignored. */ | ||
731 | int Disconnect(); | ||
732 | /* SelectTarget* */ | ||
733 | int SelectTargetX11(const char *exec, const char *args=NULL, | ||
734 | const char *auxtty=NULL); | ||
735 | int SelectTargetLinux(const char *exec, const char *args, | ||
736 | const char *auxtty=NULL); | ||
737 | int SelectTargetRemote(const char *exec, const char *rparams, | ||
738 | const char *rtype=NULL, bool download=false); | ||
739 | // TODO: Linux PIDs can be represented as intergers. What should I use? | ||
740 | // ato_pid_t doesn't exist ;-) | ||
741 | mi_frames *SelectTargetPID(const char *exec, int pid); | ||
742 | int TargetUnselect(); | ||
743 | int Run(); | ||
744 | int Stop(); | ||
745 | int Poll(mi_stop *&rs); | ||
746 | int Continue(); | ||
747 | int RunOrContinue(); | ||
748 | int Kill(); | ||
749 | mi_bkpt *Breakpoint(const char *file, int line); | ||
750 | mi_bkpt *Breakpoint(const char *where, bool temporary=false, const char *cond=NULL, | ||
751 | int count=-1, int thread=-1, bool hard_assist=false); | ||
752 | mi_bkpt *BreakpointFull(const char *file, int line, bool temporary=false, | ||
753 | const char *cond=NULL, int count=-1, int thread=-1, | ||
754 | bool hard_assist=false); | ||
755 | mi_bkpt *Breakpoint(mi_bkpt *b); | ||
756 | int BreakDelete(mi_bkpt *b); | ||
757 | int BreakAfter(mi_bkpt *b) | ||
758 | { | ||
759 | if (state!=target_specified && state!=stopped) | ||
760 | return 0; | ||
761 | return gmi_break_set_times(h,b->number,b->ignore); | ||
762 | } | ||
763 | mi_wp *Watchpoint(enum mi_wp_mode mode, const char *exp); | ||
764 | int WatchDelete(mi_wp *w); | ||
765 | int RunToMain(); | ||
766 | int StepOver(bool inst=false); | ||
767 | int TraceInto(bool inst=false); | ||
768 | int GoTo(const char *file, int line); | ||
769 | int GoTo(void *addr); | ||
770 | int FinishFun(); | ||
771 | mi_frames *ReturnNow(); | ||
772 | mi_frames *CallStack(bool args); | ||
773 | char *EvalExpression(const char *exp); | ||
774 | char *ModifyExpression(char *exp, char *newVal); | ||
775 | mi_gvar *AddgVar(const char *exp, int frame=-1) | ||
776 | { | ||
777 | if (state!=stopped) | ||
778 | return NULL; | ||
779 | return gmi_full_var_create(h,frame,exp); | ||
780 | } | ||
781 | int DelgVar(mi_gvar *var) | ||
782 | { | ||
783 | if (state!=stopped) | ||
784 | return 0; | ||
785 | return gmi_var_delete(h,var); | ||
786 | } | ||
787 | int EvalgVar(mi_gvar *var) | ||
788 | { | ||
789 | if (state!=stopped) | ||
790 | return 0; | ||
791 | return gmi_var_evaluate_expression(h,var); | ||
792 | } | ||
793 | int GetChildgVar(mi_gvar *var) | ||
794 | { | ||
795 | if (state!=stopped) | ||
796 | return 0; | ||
797 | return gmi_var_list_children(h,var); | ||
798 | } | ||
799 | int FillTypeVal(mi_gvar *var); | ||
800 | int FillOneTypeVal(mi_gvar *var); | ||
801 | int FillAttr(mi_gvar *var) | ||
802 | { | ||
803 | if (state!=stopped) | ||
804 | return 0; | ||
805 | return gmi_var_show_attributes(h,var); | ||
806 | } | ||
807 | int FillFormat(mi_gvar *var) | ||
808 | { | ||
809 | if (state!=stopped) | ||
810 | return 0; | ||
811 | return gmi_var_show_format(h,var); | ||
812 | } | ||
813 | int SetFormatgVar(mi_gvar *var, enum mi_gvar_fmt format) | ||
814 | { | ||
815 | if (state!=stopped) | ||
816 | return 0; | ||
817 | return gmi_var_set_format(h,var,format); | ||
818 | } | ||
819 | int ListChangedgVar(mi_gvar_chg *&changed) | ||
820 | { | ||
821 | if (state!=stopped) | ||
822 | return 0; | ||
823 | return gmi_var_update(h,NULL,&changed); | ||
824 | } | ||
825 | int AssigngVar(mi_gvar *var, const char *exp); | ||
826 | int Send(const char *command); | ||
827 | int Version() | ||
828 | { | ||
829 | if (state==running || state==disconnected) | ||
830 | return 0; | ||
831 | return gmi_gdb_version(h); | ||
832 | } | ||
833 | int PathSources(const char *path) | ||
834 | { | ||
835 | if (state==running || state==disconnected) | ||
836 | return 0; | ||
837 | return gmi_dir(h,path); | ||
838 | } | ||
839 | int ReadMemory(const char *exp, unsigned size, unsigned char *dest, | ||
840 | int &na, int convAddr, unsigned long *addr) | ||
841 | { | ||
842 | if (state!=stopped) | ||
843 | return 0; | ||
844 | return gmi_read_memory(h,exp,size,dest,&na,convAddr,addr); | ||
845 | } | ||
846 | char *Show(const char *var); | ||
847 | int ThreadListIDs(int *&list) | ||
848 | { | ||
849 | if (state!=stopped) | ||
850 | return 0; | ||
851 | return gmi_thread_list_ids(h,&list); | ||
852 | } | ||
853 | mi_frames *ThreadList() | ||
854 | { | ||
855 | if (state!=stopped) | ||
856 | return 0; | ||
857 | return gmi_thread_list_all_threads(h); | ||
858 | } | ||
859 | mi_frames *ThreadSelect(int id) | ||
860 | { | ||
861 | if (state!=stopped) | ||
862 | return NULL; | ||
863 | return gmi_thread_select(h,id); | ||
864 | } | ||
865 | mi_asm_insns *Disassemble(const char *start, const char *end, int mode) | ||
866 | { | ||
867 | if (state!=stopped) | ||
868 | return NULL; | ||
869 | return gmi_data_disassemble_se(h,start,end,mode); | ||
870 | } | ||
871 | mi_asm_insns *Disassemble(const char *file, int line, int lines, int mode) | ||
872 | { | ||
873 | if (state!=stopped) | ||
874 | return NULL; | ||
875 | return gmi_data_disassemble_fl(h,file,line,lines,mode); | ||
876 | } | ||
877 | mi_chg_reg *GetRegisterNames(int *how_many) | ||
878 | { | ||
879 | if (state!=target_specified && state!=stopped) | ||
880 | return NULL; | ||
881 | return gmi_data_list_register_names(h,how_many); | ||
882 | } | ||
883 | int GetRegisterNames(mi_chg_reg *chg) | ||
884 | { | ||
885 | if (state!=target_specified && state!=stopped) | ||
886 | return 0; | ||
887 | return gmi_data_list_register_names_l(h,chg); | ||
888 | } | ||
889 | int GetRegisterValues(mi_chg_reg *chg) | ||
890 | { | ||
891 | if (state!=stopped) | ||
892 | return 0; | ||
893 | return gmi_data_list_register_values(h,fm_natural,chg); | ||
894 | } | ||
895 | mi_chg_reg *GetRegisterValues(int *how_many) | ||
896 | { | ||
897 | if (state!=stopped) | ||
898 | return 0; | ||
899 | return gmi_data_list_all_register_values(h,fm_natural,how_many); | ||
900 | } | ||
901 | mi_chg_reg *GetChangedRegisters() | ||
902 | { | ||
903 | if (state!=stopped) | ||
904 | return NULL; | ||
905 | mi_chg_reg *chg=gmi_data_list_changed_registers(h); | ||
906 | if (chg && !gmi_data_list_register_values(h,fm_natural,chg)) | ||
907 | { | ||
908 | mi_free_chg_reg(chg); | ||
909 | chg=NULL; | ||
910 | } | ||
911 | return chg; | ||
912 | } | ||
913 | int UpdateRegisters(mi_chg_reg *regs); | ||
914 | |||
915 | endianType GetTargetEndian(); | ||
916 | archType GetTargetArchitecture(); | ||
917 | eState GetState() { return state; } | ||
918 | |||
919 | /* Some wrappers */ | ||
920 | static void SetGDBExe(const char *name) { mi_set_gdb_exe(name); } | ||
921 | static const char *GetGDBExe() { return mi_get_gdb_exe(); } | ||
922 | static void SetXTermExe(const char *name) { mi_set_xterm_exe(name); } | ||
923 | static const char *GetXTermExe() { return mi_get_xterm_exe(); } | ||
924 | static void SetGDBStartFile(const char *name) { mi_set_gdb_start(name); } | ||
925 | static const char *GetGDBStartFile() { return mi_get_gdb_start(); } | ||
926 | static void SetGDBConnFile(const char *name) { mi_set_gdb_conn(name); } | ||
927 | static const char *GetGDBConnFile() { return mi_get_gdb_conn(); } | ||
928 | static void SetMainFunc(const char *name) { mi_set_main_func(name); } | ||
929 | static const char *GetMainFunc() { return mi_get_main_func(); } | ||
930 | |||
931 | static const char *GetErrorStr() { return mi_get_error_str(); } | ||
932 | static const char *GetGDBError() { return mi_error_from_gdb; } | ||
933 | static int GetErrorNumber() { return mi_error; } | ||
934 | int GetErrorNumberSt(); | ||
935 | void SetConsoleCB(stream_cb cb, void *data=NULL) | ||
936 | { mi_set_console_cb(h,cb,data); } | ||
937 | void SetTargetCB(stream_cb cb, void *data=NULL) | ||
938 | { mi_set_target_cb(h,cb,data); } | ||
939 | void SetLogCB(stream_cb cb, void *data=NULL) | ||
940 | { mi_set_log_cb(h,cb,data); } | ||
941 | void SetAsyncCB(async_cb cb, void *data=NULL) | ||
942 | { mi_set_async_cb(h,cb,data); } | ||
943 | void SetToGDBCB(stream_cb cb, void *data=NULL) | ||
944 | { mi_set_to_gdb_cb(h,cb,data); } | ||
945 | void SetFromGDBCB(stream_cb cb, void *data=NULL) | ||
946 | { mi_set_from_gdb_cb(h,cb,data); } | ||
947 | void SetTimeOutCB(tm_cb cb, void *data) | ||
948 | { mi_set_time_out_cb(h,cb,data); } | ||
949 | void SetTimeOut(int to) | ||
950 | { mi_set_time_out(h,to); } | ||
951 | void ForceMIVersion(unsigned vMajor, unsigned vMiddle, unsigned vMinor) | ||
952 | { mi_force_version(h,vMajor,vMiddle,vMinor); } | ||
953 | |||
954 | const char *GetAuxTTY() | ||
955 | { return aux_tty ? aux_tty->tty : NULL; } | ||
956 | |||
957 | protected: | ||
958 | eState state; | ||
959 | dMode mode; | ||
960 | endianType targetEndian; | ||
961 | archType targetArch; | ||
962 | bool preRun; // Remote targets starts running but outside main. | ||
963 | mi_h *h; | ||
964 | mi_aux_term *aux_tty; | ||
965 | int waitingTempBkpt; | ||
966 | |||
967 | int SelectTargetTTY(const char *exec, const char *args, const char *auxtty, | ||
968 | dMode m); | ||
969 | }; | ||
970 | |||
971 | #endif | ||
972 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Miscellaneous commands. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Miscellaneous Commands" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | |||
13 | -gdb-exit Yes | ||
14 | -gdb-set Yes | ||
15 | -gdb-show Yes | ||
16 | -gdb-version Yes | ||
17 | @</pre> | ||
18 | |||
19 | GDB Bug workaround for "-gdb-show architecture": gdb 6.1 and olders doesn't | ||
20 | report it in "value", but they give the output of "show architecture". In | ||
21 | 6.4 we observed that not even a clue is reported. So now we always use | ||
22 | "show architecture". | ||
23 | |||
24 | ***************************************************************************/ | ||
25 | |||
26 | #include <string.h> | ||
27 | #include "mi_gdb.h" | ||
28 | |||
29 | /* Low level versions. */ | ||
30 | |||
31 | void mi_gdb_exit(mi_h *h) | ||
32 | { | ||
33 | mi_send(h,"-gdb-exit\n"); | ||
34 | } | ||
35 | |||
36 | void mi_gdb_version(mi_h *h) | ||
37 | { | ||
38 | mi_send(h,"-gdb-version\n"); | ||
39 | } | ||
40 | |||
41 | void mi_gdb_set(mi_h *h, const char *var, const char *val) | ||
42 | { | ||
43 | mi_send(h,"-gdb-set %s %s\n",var,val); | ||
44 | } | ||
45 | |||
46 | void mi_gdb_show(mi_h *h, const char *var) | ||
47 | { | ||
48 | if (strcmp(var,"architecture")==0) | ||
49 | mi_send(h,"show %s\n",var); | ||
50 | else | ||
51 | mi_send(h,"-gdb-show %s\n",var); | ||
52 | } | ||
53 | |||
54 | /* High level versions. */ | ||
55 | |||
56 | /**[txh]******************************************************************** | ||
57 | |||
58 | Description: | ||
59 | Exit gdb killing the child is it is running. | ||
60 | |||
61 | Command: -gdb-exit | ||
62 | |||
63 | ***************************************************************************/ | ||
64 | |||
65 | void gmi_gdb_exit(mi_h *h) | ||
66 | { | ||
67 | mi_gdb_exit(h); | ||
68 | mi_res_simple_exit(h); | ||
69 | } | ||
70 | |||
71 | /**[txh]******************************************************************** | ||
72 | |||
73 | Description: | ||
74 | Send the version to the console. | ||
75 | |||
76 | Command: -gdb-version | ||
77 | Return: !=0 OK | ||
78 | |||
79 | ***************************************************************************/ | ||
80 | |||
81 | int gmi_gdb_version(mi_h *h) | ||
82 | { | ||
83 | mi_gdb_version(h); | ||
84 | return mi_res_simple_done(h); | ||
85 | } | ||
86 | |||
87 | /**[txh]******************************************************************** | ||
88 | |||
89 | Description: | ||
90 | Set a gdb variable. | ||
91 | |||
92 | Command: -gdb-set | ||
93 | Return: !=0 OK | ||
94 | |||
95 | ***************************************************************************/ | ||
96 | |||
97 | int gmi_gdb_set(mi_h *h, const char *var, const char *val) | ||
98 | { | ||
99 | mi_gdb_set(h,var,val); | ||
100 | return mi_res_simple_done(h); | ||
101 | } | ||
102 | |||
103 | /**[txh]******************************************************************** | ||
104 | |||
105 | Description: | ||
106 | Get a gdb variable. | ||
107 | |||
108 | Command: -gdb-show | ||
109 | Return: The current value of the variable or NULL on error. | ||
110 | |||
111 | ***************************************************************************/ | ||
112 | |||
113 | char *gmi_gdb_show(mi_h *h, const char *var) | ||
114 | { | ||
115 | mi_gdb_show(h,var); | ||
116 | return mi_res_value(h); | ||
117 | } | ||
118 | |||
diff --git a/src/monkey/monkey.h b/src/monkey/monkey.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/monkey/monkey.h | |||
diff --git a/src/monkey/monkey_api.c b/src/monkey/monkey_api.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/monkey/monkey_api.c | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2007 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Parser. | ||
7 | Comments: | ||
8 | Parses the output of gdb. It basically converts the text from gdb into a | ||
9 | tree (could be a complex one) that we can easily interpret using C code. | ||
10 | |||
11 | ***************************************************************************/ | ||
12 | |||
13 | #include <ctype.h> | ||
14 | #include <string.h> | ||
15 | #include <assert.h> | ||
16 | #include "mi_gdb.h" | ||
17 | |||
18 | mi_results *mi_get_result(const char *str, const char **end); | ||
19 | int mi_get_value(mi_results *r, const char *str, const char **end); | ||
20 | |||
21 | |||
22 | /* GDB BUG!!!! I got: | ||
23 | ^error,msg="Problem parsing arguments: data-evaluate-expression ""1+2""" | ||
24 | Afects gdb 2002-04-01-cvs and 6.1.1 for sure. | ||
25 | That's an heuristical workaround. | ||
26 | */ | ||
27 | static inline | ||
28 | int EndOfStr(const char *s) | ||
29 | { | ||
30 | if (*s=='"') | ||
31 | { | ||
32 | s++; | ||
33 | return !*s || *s==',' || *s==']' || *s=='}'; | ||
34 | } | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | int mi_get_cstring_r(mi_results *r, const char *str, const char **end) | ||
39 | { | ||
40 | const char *s; | ||
41 | char *d; | ||
42 | int len; | ||
43 | |||
44 | if (*str!='"') | ||
45 | { | ||
46 | mi_error=MI_PARSER; | ||
47 | return 0; | ||
48 | } | ||
49 | str++; | ||
50 | /* Meassure. */ | ||
51 | for (s=str, len=0; *s && !EndOfStr(s); s++) | ||
52 | { | ||
53 | if (*s=='\\') | ||
54 | { | ||
55 | if (!*s) | ||
56 | { | ||
57 | mi_error=MI_PARSER; | ||
58 | return 0; | ||
59 | } | ||
60 | s++; | ||
61 | } | ||
62 | len++; | ||
63 | } | ||
64 | /* Copy. */ | ||
65 | r->type=t_const; | ||
66 | d=r->v.cstr=mi_malloc(len+1); | ||
67 | if (!r->v.cstr) | ||
68 | return 0; | ||
69 | for (s=str; *s && !EndOfStr(s); s++, d++) | ||
70 | { | ||
71 | if (*s=='\\') | ||
72 | { | ||
73 | s++; | ||
74 | switch (*s) | ||
75 | { | ||
76 | case 'n': | ||
77 | *d='\n'; | ||
78 | break; | ||
79 | case 't': | ||
80 | *d='\t'; | ||
81 | break; | ||
82 | default: | ||
83 | *d=*s; | ||
84 | } | ||
85 | } | ||
86 | else | ||
87 | *d=*s; | ||
88 | } | ||
89 | *d=0; | ||
90 | if (end) | ||
91 | *end=s+1; | ||
92 | |||
93 | return 1; | ||
94 | } | ||
95 | |||
96 | /* TODO: What's a valid variable name? | ||
97 | I'll assume a-zA-Z0-9_- */ | ||
98 | inline | ||
99 | int mi_is_var_name_char(char c) | ||
100 | { | ||
101 | return isalnum(c) || c=='-' || c=='_'; | ||
102 | } | ||
103 | |||
104 | char *mi_get_var_name(const char *str, const char **end) | ||
105 | { | ||
106 | const char *s; | ||
107 | char *r; | ||
108 | int l; | ||
109 | /* Meassure. */ | ||
110 | for (s=str; *s && mi_is_var_name_char(*s); s++); | ||
111 | if (*s!='=') | ||
112 | { | ||
113 | mi_error=MI_PARSER; | ||
114 | return NULL; | ||
115 | } | ||
116 | /* Allocate. */ | ||
117 | l=s-str; | ||
118 | r=mi_malloc(l+1); | ||
119 | /* Copy. */ | ||
120 | memcpy(r,str,l); | ||
121 | r[l]=0; | ||
122 | if (end) | ||
123 | *end=s+1; | ||
124 | return r; | ||
125 | } | ||
126 | |||
127 | |||
128 | int mi_get_list_res(mi_results *r, const char *str, const char **end, char closeC) | ||
129 | { | ||
130 | mi_results *last_r, *rs; | ||
131 | |||
132 | last_r=NULL; | ||
133 | do | ||
134 | { | ||
135 | rs=mi_get_result(str,&str); | ||
136 | if (last_r) | ||
137 | last_r->next=rs; | ||
138 | else | ||
139 | r->v.rs=rs; | ||
140 | last_r=rs; | ||
141 | if (*str==closeC) | ||
142 | { | ||
143 | *end=str+1; | ||
144 | return 1; | ||
145 | } | ||
146 | if (*str!=',') | ||
147 | break; | ||
148 | str++; | ||
149 | } | ||
150 | while (1); | ||
151 | |||
152 | mi_error=MI_PARSER; | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | #ifdef __APPLE__ | ||
157 | int mi_get_tuple_val(mi_results *r, const char *str, const char **end) | ||
158 | { | ||
159 | mi_results *last_r, *rs; | ||
160 | |||
161 | last_r=NULL; | ||
162 | do | ||
163 | { | ||
164 | rs=mi_alloc_results(); | ||
165 | if (!rs || !mi_get_value(rs,str,&str)) | ||
166 | { | ||
167 | mi_free_results(rs); | ||
168 | return 0; | ||
169 | } | ||
170 | /* Note that rs->var is NULL, that indicates that's just a value and not | ||
171 | a result. */ | ||
172 | if (last_r) | ||
173 | last_r->next=rs; | ||
174 | else | ||
175 | r->v.rs=rs; | ||
176 | last_r=rs; | ||
177 | if (*str=='}') | ||
178 | { | ||
179 | *end=str+1; | ||
180 | return 1; | ||
181 | } | ||
182 | if (*str!=',') | ||
183 | break; | ||
184 | str++; | ||
185 | } | ||
186 | while (1); | ||
187 | |||
188 | mi_error=MI_PARSER; | ||
189 | return 0; | ||
190 | } | ||
191 | #endif /* __APPLE__ */ | ||
192 | |||
193 | int mi_get_tuple(mi_results *r, const char *str, const char **end) | ||
194 | { | ||
195 | if (*str!='{') | ||
196 | { | ||
197 | mi_error=MI_PARSER; | ||
198 | return 0; | ||
199 | } | ||
200 | r->type=t_tuple; | ||
201 | str++; | ||
202 | if (*str=='}') | ||
203 | {/* Special case: empty tuple */ | ||
204 | *end=str+1; | ||
205 | return 1; | ||
206 | } | ||
207 | #ifdef __APPLE__ | ||
208 | if (mi_is_var_name_char(*str)) | ||
209 | return mi_get_list_res(r,str,end,'}'); | ||
210 | return mi_get_tuple_val(r,str,end); | ||
211 | #else /* __APPLE__ */ | ||
212 | return mi_get_list_res(r,str,end,'}'); | ||
213 | #endif /* __APPLE__ */ | ||
214 | } | ||
215 | |||
216 | int mi_get_list_val(mi_results *r, const char *str, const char **end) | ||
217 | { | ||
218 | mi_results *last_r, *rs; | ||
219 | |||
220 | last_r=NULL; | ||
221 | do | ||
222 | { | ||
223 | rs=mi_alloc_results(); | ||
224 | if (!rs || !mi_get_value(rs,str,&str)) | ||
225 | { | ||
226 | mi_free_results(rs); | ||
227 | return 0; | ||
228 | } | ||
229 | /* Note that rs->var is NULL, that indicates that's just a value and not | ||
230 | a result. */ | ||
231 | if (last_r) | ||
232 | last_r->next=rs; | ||
233 | else | ||
234 | r->v.rs=rs; | ||
235 | last_r=rs; | ||
236 | if (*str==']') | ||
237 | { | ||
238 | *end=str+1; | ||
239 | return 1; | ||
240 | } | ||
241 | if (*str!=',') | ||
242 | break; | ||
243 | str++; | ||
244 | } | ||
245 | while (1); | ||
246 | |||
247 | mi_error=MI_PARSER; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | int mi_get_list(mi_results *r, const char *str, const char **end) | ||
252 | { | ||
253 | if (*str!='[') | ||
254 | { | ||
255 | mi_error=MI_PARSER; | ||
256 | return 0; | ||
257 | } | ||
258 | r->type=t_list; | ||
259 | str++; | ||
260 | if (*str==']') | ||
261 | {/* Special case: empty list */ | ||
262 | *end=str+1; | ||
263 | return 1; | ||
264 | } | ||
265 | /* Comment: I think they could choose () for values. Is confusing in this way. */ | ||
266 | if (mi_is_var_name_char(*str)) | ||
267 | return mi_get_list_res(r,str,end,']'); | ||
268 | return mi_get_list_val(r,str,end); | ||
269 | } | ||
270 | |||
271 | int mi_get_value(mi_results *r, const char *str, const char **end) | ||
272 | { | ||
273 | switch (str[0]) | ||
274 | { | ||
275 | case '"': | ||
276 | return mi_get_cstring_r(r,str,end); | ||
277 | case '{': | ||
278 | return mi_get_tuple(r,str,end); | ||
279 | case '[': | ||
280 | return mi_get_list(r,str,end); | ||
281 | } | ||
282 | mi_error=MI_PARSER; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | mi_results *mi_get_result(const char *str, const char **end) | ||
287 | { | ||
288 | char *var; | ||
289 | mi_results *r; | ||
290 | |||
291 | var=mi_get_var_name(str,&str); | ||
292 | if (!var) | ||
293 | return NULL; | ||
294 | |||
295 | r=mi_alloc_results(); | ||
296 | if (!r) | ||
297 | { | ||
298 | free(var); | ||
299 | return NULL; | ||
300 | } | ||
301 | r->var=var; | ||
302 | |||
303 | if (!mi_get_value(r,str,end)) | ||
304 | { | ||
305 | mi_free_results(r); | ||
306 | return NULL; | ||
307 | } | ||
308 | |||
309 | return r; | ||
310 | } | ||
311 | |||
312 | mi_output *mi_get_results_alone(mi_output *r,const char *str) | ||
313 | { | ||
314 | mi_results *last_r, *rs; | ||
315 | |||
316 | /* * results */ | ||
317 | last_r=NULL; | ||
318 | do | ||
319 | { | ||
320 | if (!*str) | ||
321 | return r; | ||
322 | if (*str!=',') | ||
323 | { | ||
324 | mi_error=MI_PARSER; | ||
325 | break; | ||
326 | } | ||
327 | str++; | ||
328 | rs=mi_get_result(str,&str); | ||
329 | if (!rs) | ||
330 | break; | ||
331 | if (!last_r) | ||
332 | r->c=rs; | ||
333 | else | ||
334 | last_r->next=rs; | ||
335 | last_r=rs; | ||
336 | } | ||
337 | while (1); | ||
338 | mi_free_output(r); | ||
339 | return NULL; | ||
340 | } | ||
341 | |||
342 | mi_output *mi_parse_result_record(mi_output *r,const char *str) | ||
343 | { | ||
344 | r->type=MI_T_RESULT_RECORD; | ||
345 | |||
346 | /* Solve the result-class. */ | ||
347 | if (strncmp(str,"done",4)==0) | ||
348 | { | ||
349 | str+=4; | ||
350 | r->tclass=MI_CL_DONE; | ||
351 | } | ||
352 | else if (strncmp(str,"running",7)==0) | ||
353 | { | ||
354 | str+=7; | ||
355 | r->tclass=MI_CL_RUNNING; | ||
356 | } | ||
357 | else if (strncmp(str,"connected",9)==0) | ||
358 | { | ||
359 | str+=9; | ||
360 | r->tclass=MI_CL_CONNECTED; | ||
361 | } | ||
362 | else if (strncmp(str,"error",5)==0) | ||
363 | { | ||
364 | str+=5; | ||
365 | r->tclass=MI_CL_ERROR; | ||
366 | } | ||
367 | else if (strncmp(str,"exit",4)==0) | ||
368 | { | ||
369 | str+=4; | ||
370 | r->tclass=MI_CL_EXIT; | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | mi_error=MI_UNKNOWN_RESULT; | ||
375 | return NULL; | ||
376 | } | ||
377 | |||
378 | return mi_get_results_alone(r,str); | ||
379 | } | ||
380 | |||
381 | mi_output *mi_parse_asyn(mi_output *r,const char *str) | ||
382 | { | ||
383 | r->type=MI_T_OUT_OF_BAND; | ||
384 | r->stype=MI_ST_ASYNC; | ||
385 | /* async-class. */ | ||
386 | if (strncmp(str,"stopped",7)==0) | ||
387 | { | ||
388 | r->tclass=MI_CL_STOPPED; | ||
389 | str+=7; | ||
390 | return mi_get_results_alone(r,str); | ||
391 | } | ||
392 | if (strncmp(str,"download",8)==0) | ||
393 | { | ||
394 | r->tclass=MI_CL_DOWNLOAD; | ||
395 | str+=8; | ||
396 | return mi_get_results_alone(r,str); | ||
397 | } | ||
398 | mi_error=MI_UNKNOWN_ASYNC; | ||
399 | mi_free_output(r); | ||
400 | return NULL; | ||
401 | } | ||
402 | |||
403 | mi_output *mi_parse_exec_asyn(mi_output *r,const char *str) | ||
404 | { | ||
405 | r->sstype=MI_SST_EXEC; | ||
406 | return mi_parse_asyn(r,str); | ||
407 | } | ||
408 | |||
409 | mi_output *mi_parse_status_asyn(mi_output *r,const char *str) | ||
410 | { | ||
411 | r->sstype=MI_SST_STATUS; | ||
412 | return mi_parse_asyn(r,str); | ||
413 | } | ||
414 | |||
415 | mi_output *mi_parse_notify_asyn(mi_output *r,const char *str) | ||
416 | { | ||
417 | r->sstype=MI_SST_NOTIFY; | ||
418 | return mi_parse_asyn(r,str); | ||
419 | } | ||
420 | |||
421 | mi_output *mi_console(mi_output *r,const char *str) | ||
422 | { | ||
423 | r->type=MI_T_OUT_OF_BAND; | ||
424 | r->stype=MI_ST_STREAM; | ||
425 | r->c=mi_alloc_results(); | ||
426 | if (!r->c || !mi_get_cstring_r(r->c,str,NULL)) | ||
427 | { | ||
428 | mi_free_output(r); | ||
429 | return NULL; | ||
430 | } | ||
431 | return r; | ||
432 | } | ||
433 | |||
434 | mi_output *mi_console_stream(mi_output *r,const char *str) | ||
435 | { | ||
436 | r->sstype=MI_SST_CONSOLE; | ||
437 | return mi_console(r,str); | ||
438 | } | ||
439 | |||
440 | mi_output *mi_target_stream(mi_output *r,const char *str) | ||
441 | { | ||
442 | r->sstype=MI_SST_TARGET; | ||
443 | return mi_console(r,str); | ||
444 | } | ||
445 | |||
446 | mi_output *mi_log_stream(mi_output *r,const char *str) | ||
447 | { | ||
448 | r->sstype=MI_SST_LOG; | ||
449 | return mi_console(r,str); | ||
450 | } | ||
451 | |||
452 | mi_output *mi_parse_gdb_output(const char *str) | ||
453 | { | ||
454 | char type=str[0]; | ||
455 | |||
456 | mi_output *r=mi_alloc_output(); | ||
457 | if (!r) | ||
458 | { | ||
459 | mi_error=MI_OUT_OF_MEMORY; | ||
460 | return NULL; | ||
461 | } | ||
462 | str++; | ||
463 | switch (type) | ||
464 | { | ||
465 | case '^': | ||
466 | return mi_parse_result_record(r,str); | ||
467 | case '*': | ||
468 | return mi_parse_exec_asyn(r,str); | ||
469 | case '+': | ||
470 | return mi_parse_status_asyn(r,str); | ||
471 | case '=': | ||
472 | return mi_parse_notify_asyn(r,str); | ||
473 | case '~': | ||
474 | return mi_console_stream(r,str); | ||
475 | case '@': | ||
476 | return mi_target_stream(r,str); | ||
477 | case '&': | ||
478 | return mi_log_stream(r,str); | ||
479 | } | ||
480 | mi_error=MI_PARSER; | ||
481 | return NULL; | ||
482 | } | ||
483 | |||
484 | mi_output *mi_get_rrecord(mi_output *r) | ||
485 | { | ||
486 | if (!r) | ||
487 | return NULL; | ||
488 | while (r) | ||
489 | { | ||
490 | if (r->type==MI_T_RESULT_RECORD) | ||
491 | return r; | ||
492 | r=r->next; | ||
493 | } | ||
494 | return r; | ||
495 | } | ||
496 | |||
497 | mi_results *mi_get_var_r(mi_results *r, const char *var) | ||
498 | { | ||
499 | while (r) | ||
500 | { | ||
501 | if (strcmp(r->var,var)==0) | ||
502 | return r; | ||
503 | r=r->next; | ||
504 | } | ||
505 | return NULL; | ||
506 | } | ||
507 | |||
508 | mi_results *mi_get_var(mi_output *res, const char *var) | ||
509 | { | ||
510 | if (!res) | ||
511 | return NULL; | ||
512 | return mi_get_var_r(res->c,var); | ||
513 | } | ||
514 | |||
515 | int mi_get_async_stop_reason(mi_output *r, char **reason) | ||
516 | { | ||
517 | int found_stopped=0; | ||
518 | |||
519 | *reason=NULL; | ||
520 | while (r) | ||
521 | { | ||
522 | if (r->type==MI_T_RESULT_RECORD && r->tclass==MI_CL_ERROR) | ||
523 | { | ||
524 | if (r->c->type==t_const) | ||
525 | *reason=r->c->v.cstr; | ||
526 | return 0; | ||
527 | } | ||
528 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
529 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
530 | { | ||
531 | mi_results *p=r->c; | ||
532 | found_stopped=1; | ||
533 | while (p) | ||
534 | { | ||
535 | if (strcmp(p->var,"reason")==0) | ||
536 | { | ||
537 | *reason=p->v.cstr; | ||
538 | return 1; | ||
539 | } | ||
540 | p=p->next; | ||
541 | } | ||
542 | } | ||
543 | r=r->next; | ||
544 | } | ||
545 | if (*reason==NULL && found_stopped) | ||
546 | { | ||
547 | *reason=strdup("unknown (temp bkpt?)"); | ||
548 | return 1; | ||
549 | } | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | mi_frames *mi_get_async_frame(mi_output *r) | ||
554 | { | ||
555 | while (r) | ||
556 | { | ||
557 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
558 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
559 | { | ||
560 | mi_results *p=r->c; | ||
561 | while (p) | ||
562 | { | ||
563 | if (strcmp(p->var,"frame")==0) | ||
564 | return mi_parse_frame(p->v.rs); | ||
565 | p=p->next; | ||
566 | } | ||
567 | } | ||
568 | r=r->next; | ||
569 | } | ||
570 | return NULL; | ||
571 | } | ||
572 | |||
573 | int mi_res_simple(mi_h *h, int tclass, int accert_ret) | ||
574 | { | ||
575 | mi_output *r, *res; | ||
576 | int ret=0; | ||
577 | |||
578 | r=mi_get_response_blk(h); | ||
579 | res=mi_get_rrecord(r); | ||
580 | |||
581 | if (res) | ||
582 | ret=res->tclass==tclass; | ||
583 | mi_free_output(r); | ||
584 | |||
585 | return ret; | ||
586 | } | ||
587 | |||
588 | |||
589 | int mi_res_simple_done(mi_h *h) | ||
590 | { | ||
591 | return mi_res_simple(h,MI_CL_DONE,0); | ||
592 | } | ||
593 | |||
594 | int mi_res_simple_exit(mi_h *h) | ||
595 | { | ||
596 | return mi_res_simple(h,MI_CL_EXIT,1); | ||
597 | } | ||
598 | |||
599 | int mi_res_simple_running(mi_h *h) | ||
600 | { | ||
601 | return mi_res_simple(h,MI_CL_RUNNING,0); | ||
602 | } | ||
603 | |||
604 | int mi_res_simple_connected(mi_h *h) | ||
605 | { | ||
606 | return mi_res_simple(h,MI_CL_CONNECTED,0); | ||
607 | } | ||
608 | |||
609 | mi_results *mi_res_var(mi_h *h, const char *var, int tclass) | ||
610 | { | ||
611 | mi_output *r, *res; | ||
612 | mi_results *the_var=NULL; | ||
613 | |||
614 | r=mi_get_response_blk(h); | ||
615 | /* All the code that follows is "NULL" tolerant. */ | ||
616 | /* Look for the result-record. */ | ||
617 | res=mi_get_rrecord(r); | ||
618 | /* Look for the desired var. */ | ||
619 | if (res && res->tclass==tclass) | ||
620 | the_var=mi_get_var(res,var); | ||
621 | /* Release all but the one we want. */ | ||
622 | mi_free_output_but(r,NULL,the_var); | ||
623 | return the_var; | ||
624 | } | ||
625 | |||
626 | mi_results *mi_res_done_var(mi_h *h, const char *var) | ||
627 | { | ||
628 | return mi_res_var(h,var,MI_CL_DONE); | ||
629 | } | ||
630 | |||
631 | mi_frames *mi_parse_frame(mi_results *c) | ||
632 | { | ||
633 | mi_frames *res=mi_alloc_frames(); | ||
634 | char *end; | ||
635 | |||
636 | if (res) | ||
637 | { | ||
638 | while (c) | ||
639 | { | ||
640 | if (c->type==t_const) | ||
641 | { | ||
642 | if (strcmp(c->var,"level")==0) | ||
643 | res->level=atoi(c->v.cstr); | ||
644 | else if (strcmp(c->var,"addr")==0) | ||
645 | res->addr=(void *)strtoul(c->v.cstr,&end,0); | ||
646 | else if (strcmp(c->var,"func")==0) | ||
647 | { | ||
648 | res->func=c->v.cstr; | ||
649 | c->v.cstr=NULL; | ||
650 | } | ||
651 | else if (strcmp(c->var,"file")==0) | ||
652 | { | ||
653 | res->file=c->v.cstr; | ||
654 | c->v.cstr=NULL; | ||
655 | } | ||
656 | else if (strcmp(c->var,"from")==0) | ||
657 | { | ||
658 | res->from=c->v.cstr; | ||
659 | c->v.cstr=NULL; | ||
660 | } | ||
661 | else if (strcmp(c->var,"line")==0) | ||
662 | res->line=atoi(c->v.cstr); | ||
663 | } | ||
664 | else if (c->type==t_list && strcmp(c->var,"args")==0) | ||
665 | { | ||
666 | res->args=c->v.rs; | ||
667 | c->v.rs=NULL; | ||
668 | } | ||
669 | c=c->next; | ||
670 | } | ||
671 | } | ||
672 | return res; | ||
673 | } | ||
674 | |||
675 | mi_frames *mi_res_frame(mi_h *h) | ||
676 | { | ||
677 | mi_results *r=mi_res_done_var(h,"frame"); | ||
678 | mi_frames *f=NULL; | ||
679 | |||
680 | if (r && r->type==t_tuple) | ||
681 | f=mi_parse_frame(r->v.rs); | ||
682 | mi_free_results(r); | ||
683 | return f; | ||
684 | } | ||
685 | |||
686 | mi_frames *mi_res_frames_array(mi_h *h, const char *var) | ||
687 | { | ||
688 | mi_results *r=mi_res_done_var(h,var), *c; | ||
689 | mi_frames *res=NULL, *nframe, *last=NULL; | ||
690 | |||
691 | if (!r) | ||
692 | return NULL; | ||
693 | #ifdef __APPLE__ | ||
694 | if (r->type!=t_list && r->type!=t_tuple) | ||
695 | #else | ||
696 | if (r->type!=t_list) | ||
697 | #endif | ||
698 | { | ||
699 | mi_free_results(r); | ||
700 | return NULL; | ||
701 | } | ||
702 | c=r->v.rs; | ||
703 | while (c) | ||
704 | { | ||
705 | if (strcmp(c->var,"frame")==0 && c->type==t_tuple) | ||
706 | { | ||
707 | nframe=mi_parse_frame(c->v.rs); | ||
708 | if (nframe) | ||
709 | { | ||
710 | if (!last) | ||
711 | res=nframe; | ||
712 | else | ||
713 | last->next=nframe; | ||
714 | last=nframe; | ||
715 | } | ||
716 | } | ||
717 | c=c->next; | ||
718 | } | ||
719 | mi_free_results(r); | ||
720 | return res; | ||
721 | } | ||
722 | |||
723 | mi_frames *mi_res_frames_list(mi_h *h) | ||
724 | { | ||
725 | mi_output *r, *res; | ||
726 | mi_frames *ret=NULL, *nframe, *last=NULL; | ||
727 | mi_results *c; | ||
728 | |||
729 | r=mi_get_response_blk(h); | ||
730 | res=mi_get_rrecord(r); | ||
731 | if (res && res->tclass==MI_CL_DONE) | ||
732 | { | ||
733 | c=res->c; | ||
734 | while (c) | ||
735 | { | ||
736 | if (strcmp(c->var,"frame")==0 && c->type==t_tuple) | ||
737 | { | ||
738 | nframe=mi_parse_frame(c->v.rs); | ||
739 | if (nframe) | ||
740 | { | ||
741 | if (!last) | ||
742 | ret=nframe; | ||
743 | else | ||
744 | last->next=nframe; | ||
745 | last=nframe; | ||
746 | } | ||
747 | } | ||
748 | c=c->next; | ||
749 | } | ||
750 | } | ||
751 | mi_free_output(r); | ||
752 | return ret; | ||
753 | } | ||
754 | |||
755 | int mi_get_thread_ids(mi_output *res, int **list) | ||
756 | { | ||
757 | mi_results *vids, *lids; | ||
758 | int ids=-1, i; | ||
759 | |||
760 | *list=NULL; | ||
761 | vids=mi_get_var(res,"number-of-threads"); | ||
762 | lids=mi_get_var(res,"thread-ids"); | ||
763 | if (vids && vids->type==t_const && | ||
764 | lids && lids->type==t_tuple) | ||
765 | { | ||
766 | ids=atoi(vids->v.cstr); | ||
767 | if (ids) | ||
768 | { | ||
769 | int *lst; | ||
770 | lst=(int *)mi_calloc(ids,sizeof(int)); | ||
771 | if (lst) | ||
772 | { | ||
773 | lids=lids->v.rs; | ||
774 | i=0; | ||
775 | while (lids) | ||
776 | { | ||
777 | if (strcmp(lids->var,"thread-id")==0 && lids->type==t_const) | ||
778 | lst[i++]=atoi(lids->v.cstr); | ||
779 | lids=lids->next; | ||
780 | } | ||
781 | *list=lst; | ||
782 | } | ||
783 | else | ||
784 | ids=-1; | ||
785 | } | ||
786 | } | ||
787 | return ids; | ||
788 | } | ||
789 | |||
790 | int mi_res_thread_ids(mi_h *h, int **list) | ||
791 | { | ||
792 | mi_output *r, *res; | ||
793 | int ids=-1; | ||
794 | |||
795 | r=mi_get_response_blk(h); | ||
796 | res=mi_get_rrecord(r); | ||
797 | if (res && res->tclass==MI_CL_DONE) | ||
798 | ids=mi_get_thread_ids(res,list); | ||
799 | mi_free_output(r); | ||
800 | return ids; | ||
801 | } | ||
802 | |||
803 | enum mi_gvar_lang mi_lang_str_to_enum(const char *lang) | ||
804 | { | ||
805 | enum mi_gvar_lang lg=lg_unknown; | ||
806 | |||
807 | if (strcmp(lang,"C")==0) | ||
808 | lg=lg_c; | ||
809 | else if (strcmp(lang,"C++")==0) | ||
810 | lg=lg_cpp; | ||
811 | else if (strcmp(lang,"Java")==0) | ||
812 | lg=lg_java; | ||
813 | |||
814 | return lg; | ||
815 | } | ||
816 | |||
817 | const char *mi_lang_enum_to_str(enum mi_gvar_lang lang) | ||
818 | { | ||
819 | const char *lg; | ||
820 | |||
821 | switch (lang) | ||
822 | { | ||
823 | case lg_c: | ||
824 | lg="C"; | ||
825 | break; | ||
826 | case lg_cpp: | ||
827 | lg="C++"; | ||
828 | break; | ||
829 | case lg_java: | ||
830 | lg="Java"; | ||
831 | break; | ||
832 | /*case lg_unknown:*/ | ||
833 | default: | ||
834 | lg="unknown"; | ||
835 | break; | ||
836 | } | ||
837 | return lg; | ||
838 | } | ||
839 | |||
840 | enum mi_gvar_fmt mi_format_str_to_enum(const char *format) | ||
841 | { | ||
842 | enum mi_gvar_fmt fmt=fm_natural; | ||
843 | |||
844 | if (strcmp(format,"binary")==0) | ||
845 | fmt=fm_binary; | ||
846 | else if (strcmp(format,"decimal")==0) | ||
847 | fmt=fm_decimal; | ||
848 | else if (strcmp(format,"hexadecimal")==0) | ||
849 | fmt=fm_hexadecimal; | ||
850 | else if (strcmp(format,"octal")==0) | ||
851 | fmt=fm_octal; | ||
852 | |||
853 | return fmt; | ||
854 | } | ||
855 | |||
856 | const char *mi_format_enum_to_str(enum mi_gvar_fmt format) | ||
857 | { | ||
858 | const char *fmt; | ||
859 | |||
860 | switch (format) | ||
861 | { | ||
862 | case fm_natural: | ||
863 | fmt="natural"; | ||
864 | break; | ||
865 | case fm_binary: | ||
866 | fmt="binary"; | ||
867 | break; | ||
868 | case fm_decimal: | ||
869 | fmt="decimal"; | ||
870 | break; | ||
871 | case fm_hexadecimal: | ||
872 | fmt="hexadecimal"; | ||
873 | break; | ||
874 | case fm_octal: | ||
875 | fmt="octal"; | ||
876 | break; | ||
877 | case fm_raw: | ||
878 | fmt="raw"; | ||
879 | break; | ||
880 | default: | ||
881 | fmt="unknown"; | ||
882 | } | ||
883 | return fmt; | ||
884 | } | ||
885 | |||
886 | char mi_format_enum_to_char(enum mi_gvar_fmt format) | ||
887 | { | ||
888 | char fmt; | ||
889 | |||
890 | switch (format) | ||
891 | { | ||
892 | case fm_natural: | ||
893 | fmt='N'; | ||
894 | break; | ||
895 | case fm_binary: | ||
896 | fmt='t'; | ||
897 | break; | ||
898 | case fm_decimal: | ||
899 | fmt='d'; | ||
900 | break; | ||
901 | case fm_hexadecimal: | ||
902 | fmt='x'; | ||
903 | break; | ||
904 | case fm_octal: | ||
905 | fmt='o'; | ||
906 | break; | ||
907 | case fm_raw: | ||
908 | fmt='r'; | ||
909 | break; | ||
910 | default: | ||
911 | fmt=' '; | ||
912 | } | ||
913 | return fmt; | ||
914 | } | ||
915 | |||
916 | mi_gvar *mi_get_gvar(mi_output *o, mi_gvar *cur, const char *expression) | ||
917 | { | ||
918 | mi_results *r; | ||
919 | mi_gvar *res=cur ? cur : mi_alloc_gvar(); | ||
920 | int l; | ||
921 | |||
922 | if (!res) | ||
923 | return res; | ||
924 | r=o->c; | ||
925 | if (expression) | ||
926 | res->exp=strdup(expression); | ||
927 | while (r) | ||
928 | { | ||
929 | if (r->type==t_const) | ||
930 | { | ||
931 | if (strcmp(r->var,"name")==0) | ||
932 | { | ||
933 | free(res->name); | ||
934 | res->name=r->v.cstr; | ||
935 | r->v.cstr=NULL; | ||
936 | } | ||
937 | else if (strcmp(r->var,"numchild")==0) | ||
938 | { | ||
939 | res->numchild=atoi(r->v.cstr); | ||
940 | } | ||
941 | else if (strcmp(r->var,"type")==0) | ||
942 | { | ||
943 | free(res->type); | ||
944 | res->type=r->v.cstr; | ||
945 | r->v.cstr=NULL; | ||
946 | l=strlen(res->type); | ||
947 | if (l && res->type[l-1]=='*') | ||
948 | res->ispointer=1; | ||
949 | } | ||
950 | else if (strcmp(r->var,"lang")==0) | ||
951 | { | ||
952 | res->lang=mi_lang_str_to_enum(r->v.cstr); | ||
953 | } | ||
954 | else if (strcmp(r->var,"exp")==0) | ||
955 | { | ||
956 | free(res->exp); | ||
957 | res->exp=r->v.cstr; | ||
958 | r->v.cstr=NULL; | ||
959 | } | ||
960 | else if (strcmp(r->var,"format")==0) | ||
961 | { | ||
962 | res->format=mi_format_str_to_enum(r->v.cstr); | ||
963 | } | ||
964 | else if (strcmp(r->var,"attr")==0) | ||
965 | { /* Note: gdb 6.1.1 have only this: */ | ||
966 | if (strcmp(r->v.cstr,"editable")==0) | ||
967 | res->attr=MI_ATTR_EDITABLE; | ||
968 | else /* noneditable */ | ||
969 | res->attr=MI_ATTR_NONEDITABLE; | ||
970 | } | ||
971 | } | ||
972 | r=r->next; | ||
973 | } | ||
974 | return res; | ||
975 | } | ||
976 | |||
977 | mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression) | ||
978 | { | ||
979 | mi_output *r, *res; | ||
980 | mi_gvar *gvar=NULL; | ||
981 | |||
982 | r=mi_get_response_blk(h); | ||
983 | res=mi_get_rrecord(r); | ||
984 | if (res && res->tclass==MI_CL_DONE) | ||
985 | gvar=mi_get_gvar(res,cur,expression); | ||
986 | mi_free_output(r); | ||
987 | return gvar; | ||
988 | } | ||
989 | |||
990 | mi_gvar_chg *mi_get_gvar_chg(mi_results *r) | ||
991 | { | ||
992 | mi_gvar_chg *n; | ||
993 | |||
994 | if (r->type!=t_const) | ||
995 | return NULL; | ||
996 | n=mi_alloc_gvar_chg(); | ||
997 | if (n) | ||
998 | { | ||
999 | while (r) | ||
1000 | { | ||
1001 | if (r->type==t_const) | ||
1002 | { | ||
1003 | if (strcmp(r->var,"name")==0) | ||
1004 | { | ||
1005 | n->name=r->v.cstr; | ||
1006 | r->v.cstr=NULL; | ||
1007 | } | ||
1008 | else if (strcmp(r->var,"in_scope")==0) | ||
1009 | { | ||
1010 | n->in_scope=strcmp(r->v.cstr,"true")==0; | ||
1011 | } | ||
1012 | else if (strcmp(r->var,"new_type")==0) | ||
1013 | { | ||
1014 | n->new_type=r->v.cstr; | ||
1015 | r->v.cstr=NULL; | ||
1016 | } | ||
1017 | else if (strcmp(r->var,"new_num_children")==0) | ||
1018 | { | ||
1019 | n->new_num_children=atoi(r->v.cstr); | ||
1020 | } | ||
1021 | // type_changed="false" is the default | ||
1022 | } | ||
1023 | r=r->next; | ||
1024 | } | ||
1025 | } | ||
1026 | return n; | ||
1027 | } | ||
1028 | |||
1029 | int mi_res_changelist(mi_h *h, mi_gvar_chg **changed) | ||
1030 | { | ||
1031 | mi_gvar_chg *last, *n; | ||
1032 | mi_results *res=mi_res_done_var(h,"changelist"), *r; | ||
1033 | int count=0; | ||
1034 | |||
1035 | *changed=NULL; | ||
1036 | if (!res) | ||
1037 | return 0; | ||
1038 | last=NULL; | ||
1039 | count=1; | ||
1040 | n=NULL; | ||
1041 | r=res->v.rs; | ||
1042 | |||
1043 | if (res->type==t_list) | ||
1044 | {// MI v2 a list of tuples | ||
1045 | while (r) | ||
1046 | { | ||
1047 | if (r->type==t_tuple) | ||
1048 | { | ||
1049 | n=mi_get_gvar_chg(r->v.rs); | ||
1050 | if (n) | ||
1051 | { | ||
1052 | if (last) | ||
1053 | last->next=n; | ||
1054 | else | ||
1055 | *changed=n; | ||
1056 | last=n; | ||
1057 | count++; | ||
1058 | } | ||
1059 | } | ||
1060 | r=r->next; | ||
1061 | } | ||
1062 | } | ||
1063 | else if (res->type==t_tuple) | ||
1064 | {// MI v1 a tuple with all together *8-P | ||
1065 | while (r) | ||
1066 | { | ||
1067 | if (r->type==t_const) /* Just in case. */ | ||
1068 | {/* Get one var. */ | ||
1069 | if (strcmp(r->var,"name")==0) | ||
1070 | { | ||
1071 | if (n) | ||
1072 | {/* Add to the list*/ | ||
1073 | if (last) | ||
1074 | last->next=n; | ||
1075 | else | ||
1076 | *changed=n; | ||
1077 | last=n; | ||
1078 | count++; | ||
1079 | } | ||
1080 | n=mi_alloc_gvar_chg(); | ||
1081 | if (!n) | ||
1082 | { | ||
1083 | mi_free_gvar_chg(*changed); | ||
1084 | return 0; | ||
1085 | } | ||
1086 | n->name=r->v.cstr; | ||
1087 | r->v.cstr=NULL; | ||
1088 | } | ||
1089 | else if (strcmp(r->var,"in_scope")==0) | ||
1090 | { | ||
1091 | n->in_scope=strcmp(r->v.cstr,"true")==0; | ||
1092 | } | ||
1093 | else if (strcmp(r->var,"new_type")==0) | ||
1094 | { | ||
1095 | n->new_type=r->v.cstr; | ||
1096 | r->v.cstr=NULL; | ||
1097 | } | ||
1098 | else if (strcmp(r->var,"new_num_children")==0) | ||
1099 | { | ||
1100 | n->new_num_children=atoi(r->v.cstr); | ||
1101 | } | ||
1102 | // type_changed="false" is the default | ||
1103 | } | ||
1104 | r=r->next; | ||
1105 | } | ||
1106 | if (n) | ||
1107 | {/* Add to the list*/ | ||
1108 | if (last) | ||
1109 | last->next=n; | ||
1110 | else | ||
1111 | *changed=n; | ||
1112 | last=n; | ||
1113 | count++; | ||
1114 | } | ||
1115 | } | ||
1116 | mi_free_results(res); | ||
1117 | |||
1118 | return count; | ||
1119 | } | ||
1120 | |||
1121 | int mi_get_children(mi_results *ch, mi_gvar *v) | ||
1122 | { | ||
1123 | mi_gvar *cur=NULL, *aux; | ||
1124 | int i=0, count=v->numchild, l; | ||
1125 | |||
1126 | while (ch) | ||
1127 | { | ||
1128 | if (strcmp(ch->var,"child")==0 && ch->type==t_tuple && i<count) | ||
1129 | { | ||
1130 | mi_results *r=ch->v.rs; | ||
1131 | aux=mi_alloc_gvar(); | ||
1132 | if (!aux) | ||
1133 | return 0; | ||
1134 | if (!v->child) | ||
1135 | v->child=aux; | ||
1136 | else | ||
1137 | cur->next=aux; | ||
1138 | cur=aux; | ||
1139 | cur->parent=v; | ||
1140 | cur->depth=v->depth+1; | ||
1141 | |||
1142 | while (r) | ||
1143 | { | ||
1144 | if (r->type==t_const) | ||
1145 | { | ||
1146 | if (strcmp(r->var,"name")==0) | ||
1147 | { | ||
1148 | cur->name=r->v.cstr; | ||
1149 | r->v.cstr=NULL; | ||
1150 | } | ||
1151 | else if (strcmp(r->var,"exp")==0) | ||
1152 | { | ||
1153 | cur->exp=r->v.cstr; | ||
1154 | r->v.cstr=NULL; | ||
1155 | } | ||
1156 | else if (strcmp(r->var,"type")==0) | ||
1157 | { | ||
1158 | cur->type=r->v.cstr; | ||
1159 | r->v.cstr=NULL; | ||
1160 | l=strlen(cur->type); | ||
1161 | if (l && cur->type[l-1]=='*') | ||
1162 | cur->ispointer=1; | ||
1163 | } | ||
1164 | else if (strcmp(r->var,"value")==0) | ||
1165 | { | ||
1166 | cur->value=r->v.cstr; | ||
1167 | r->v.cstr=NULL; | ||
1168 | } | ||
1169 | else if (strcmp(r->var,"numchild")==0) | ||
1170 | { | ||
1171 | cur->numchild=atoi(r->v.cstr); | ||
1172 | } | ||
1173 | } | ||
1174 | r=r->next; | ||
1175 | } | ||
1176 | i++; | ||
1177 | } | ||
1178 | ch=ch->next; | ||
1179 | } | ||
1180 | v->vischild=i; | ||
1181 | v->opened=1; | ||
1182 | return i==v->numchild; | ||
1183 | } | ||
1184 | |||
1185 | int mi_res_children(mi_h *h, mi_gvar *v) | ||
1186 | { | ||
1187 | mi_output *r, *res; | ||
1188 | int ok=0; | ||
1189 | |||
1190 | r=mi_get_response_blk(h); | ||
1191 | res=mi_get_rrecord(r); | ||
1192 | if (res && res->tclass==MI_CL_DONE) | ||
1193 | { | ||
1194 | mi_results *num=mi_get_var(res,"numchild"); | ||
1195 | if (num && num->type==t_const) | ||
1196 | { | ||
1197 | v->numchild=atoi(num->v.cstr); | ||
1198 | if (v->child) | ||
1199 | { | ||
1200 | mi_free_gvar(v->child); | ||
1201 | v->child=NULL; | ||
1202 | } | ||
1203 | if (v->numchild) | ||
1204 | { | ||
1205 | mi_results *ch =mi_get_var(res,"children"); | ||
1206 | if (ch && ch->type!=t_const) /* MI v1 tuple, MI v2 list */ | ||
1207 | ok=mi_get_children(ch->v.rs,v); | ||
1208 | } | ||
1209 | else | ||
1210 | ok=1; | ||
1211 | } | ||
1212 | } | ||
1213 | mi_free_output(r); | ||
1214 | return ok; | ||
1215 | } | ||
1216 | |||
1217 | mi_bkpt *mi_get_bkpt(mi_results *p) | ||
1218 | { | ||
1219 | mi_bkpt *res; | ||
1220 | char *end; | ||
1221 | |||
1222 | res=mi_alloc_bkpt(); | ||
1223 | if (!res) | ||
1224 | return NULL; | ||
1225 | while (p) | ||
1226 | { | ||
1227 | if (p->type==t_const && p->var) | ||
1228 | { | ||
1229 | if (strcmp(p->var,"number")==0) | ||
1230 | res->number=atoi(p->v.cstr); | ||
1231 | else if (strcmp(p->var,"type")==0) | ||
1232 | { | ||
1233 | if (strcmp(p->v.cstr,"breakpoint")==0) | ||
1234 | res->type=t_breakpoint; | ||
1235 | else | ||
1236 | res->type=t_unknown; | ||
1237 | } | ||
1238 | else if (strcmp(p->var,"disp")==0) | ||
1239 | { | ||
1240 | if (strcmp(p->v.cstr,"keep")==0) | ||
1241 | res->disp=d_keep; | ||
1242 | else if (strcmp(p->v.cstr,"del")==0) | ||
1243 | res->disp=d_del; | ||
1244 | else | ||
1245 | res->disp=d_unknown; | ||
1246 | } | ||
1247 | else if (strcmp(p->var,"enabled")==0) | ||
1248 | res->enabled=p->v.cstr[0]=='y'; | ||
1249 | else if (strcmp(p->var,"addr")==0) | ||
1250 | res->addr=(void *)strtoul(p->v.cstr,&end,0); | ||
1251 | else if (strcmp(p->var,"func")==0) | ||
1252 | { | ||
1253 | res->func=p->v.cstr; | ||
1254 | p->v.cstr=NULL; | ||
1255 | } | ||
1256 | else if (strcmp(p->var,"file")==0) | ||
1257 | { | ||
1258 | res->file=p->v.cstr; | ||
1259 | p->v.cstr=NULL; | ||
1260 | } | ||
1261 | else if (strcmp(p->var,"line")==0) | ||
1262 | res->line=atoi(p->v.cstr); | ||
1263 | else if (strcmp(p->var,"times")==0) | ||
1264 | res->times=atoi(p->v.cstr); | ||
1265 | else if (strcmp(p->var,"ignore")==0) | ||
1266 | res->ignore=atoi(p->v.cstr); | ||
1267 | else if (strcmp(p->var,"cond")==0) | ||
1268 | { | ||
1269 | res->cond=p->v.cstr; | ||
1270 | p->v.cstr=NULL; | ||
1271 | } | ||
1272 | } | ||
1273 | p=p->next; | ||
1274 | } | ||
1275 | return res; | ||
1276 | } | ||
1277 | |||
1278 | mi_bkpt *mi_res_bkpt(mi_h *h) | ||
1279 | { | ||
1280 | mi_results *r=mi_res_done_var(h,"bkpt"); | ||
1281 | mi_bkpt *b=NULL; | ||
1282 | |||
1283 | if (r && r->type==t_tuple) | ||
1284 | b=mi_get_bkpt(r->v.rs); | ||
1285 | mi_free_results(r); | ||
1286 | return b; | ||
1287 | } | ||
1288 | |||
1289 | mi_wp *mi_get_wp(mi_results *p, enum mi_wp_mode m) | ||
1290 | { | ||
1291 | mi_wp *res=mi_alloc_wp(); | ||
1292 | |||
1293 | if (res) | ||
1294 | { | ||
1295 | res->mode=m; | ||
1296 | while (p) | ||
1297 | { | ||
1298 | if (p->type==t_const && p->var) | ||
1299 | { | ||
1300 | if (strcmp(p->var,"number")==0) | ||
1301 | { | ||
1302 | res->number=atoi(p->v.cstr); | ||
1303 | res->enabled=1; | ||
1304 | } | ||
1305 | else if (strcmp(p->var,"exp")==0) | ||
1306 | { | ||
1307 | res->exp=p->v.cstr; | ||
1308 | p->v.cstr=NULL; | ||
1309 | } | ||
1310 | } | ||
1311 | p=p->next; | ||
1312 | } | ||
1313 | } | ||
1314 | return res; | ||
1315 | } | ||
1316 | |||
1317 | mi_wp *mi_parse_wp_res(mi_output *r) | ||
1318 | { | ||
1319 | mi_results *p; | ||
1320 | enum mi_wp_mode m=wm_unknown; | ||
1321 | |||
1322 | /* The info is in a result wpt=... */ | ||
1323 | p=r->c; | ||
1324 | while (p) | ||
1325 | { | ||
1326 | if (p->var) | ||
1327 | { | ||
1328 | if (strcmp(p->var,"wpt")==0) | ||
1329 | m=wm_write; | ||
1330 | else if (strcmp(p->var,"hw-rwpt")==0) | ||
1331 | m=wm_read; | ||
1332 | else if (strcmp(p->var,"hw-awpt")==0) | ||
1333 | m=wm_rw; | ||
1334 | if (m!=wm_unknown) | ||
1335 | break; | ||
1336 | } | ||
1337 | p=p->next; | ||
1338 | } | ||
1339 | if (!p || p->type!=t_tuple) | ||
1340 | return NULL; | ||
1341 | /* Scan the values inside it. */ | ||
1342 | return mi_get_wp(p->v.rs,m); | ||
1343 | } | ||
1344 | |||
1345 | mi_wp *mi_res_wp(mi_h *h) | ||
1346 | { | ||
1347 | mi_output *r, *res; | ||
1348 | mi_wp *ret=NULL; | ||
1349 | |||
1350 | r=mi_get_response_blk(h); | ||
1351 | res=mi_get_rrecord(r); | ||
1352 | |||
1353 | if (res) | ||
1354 | ret=mi_parse_wp_res(res); | ||
1355 | |||
1356 | mi_free_output(r); | ||
1357 | return ret; | ||
1358 | } | ||
1359 | |||
1360 | char *mi_res_value(mi_h *h) | ||
1361 | { | ||
1362 | mi_results *r=mi_res_done_var(h,"value"); | ||
1363 | char *s=NULL; | ||
1364 | |||
1365 | if (r && r->type==t_const) | ||
1366 | { | ||
1367 | s=r->v.cstr; | ||
1368 | r->v.rs=NULL; | ||
1369 | } | ||
1370 | mi_free_results(r); | ||
1371 | return s; | ||
1372 | } | ||
1373 | |||
1374 | mi_output *mi_get_stop_record(mi_output *r) | ||
1375 | { | ||
1376 | while (r) | ||
1377 | { | ||
1378 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
1379 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
1380 | return r; | ||
1381 | r=r->next; | ||
1382 | } | ||
1383 | return r; | ||
1384 | } | ||
1385 | |||
1386 | static | ||
1387 | char *reason_names[]= | ||
1388 | { | ||
1389 | "breakpoint-hit", | ||
1390 | "watchpoint-trigger", | ||
1391 | "read-watchpoint-trigger", | ||
1392 | "access-watchpoint-trigger", | ||
1393 | "watchpoint-scope", | ||
1394 | "function-finished", | ||
1395 | "location-reached", | ||
1396 | "end-stepping-range", | ||
1397 | "exited-signalled", | ||
1398 | "exited", | ||
1399 | "exited-normally", | ||
1400 | "signal-received" | ||
1401 | }; | ||
1402 | |||
1403 | static | ||
1404 | enum mi_stop_reason reason_values[]= | ||
1405 | { | ||
1406 | sr_bkpt_hit, | ||
1407 | sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, | ||
1408 | sr_function_finished, sr_location_reached, sr_end_stepping_range, | ||
1409 | sr_exited_signalled, sr_exited, sr_exited_normally, | ||
1410 | sr_signal_received | ||
1411 | }; | ||
1412 | |||
1413 | static | ||
1414 | char *reason_expl[]= | ||
1415 | { | ||
1416 | "Hit a breakpoint", | ||
1417 | "Write watchpoint", | ||
1418 | "Read watchpoint", | ||
1419 | "Access watchpoint", | ||
1420 | "Watchpoint out of scope", | ||
1421 | "Function finished", | ||
1422 | "Location reached", | ||
1423 | "End of stepping", | ||
1424 | "Exited signalled", | ||
1425 | "Exited with error", | ||
1426 | "Exited normally", | ||
1427 | "Signal received" | ||
1428 | }; | ||
1429 | |||
1430 | enum mi_stop_reason mi_reason_str_to_enum(const char *s) | ||
1431 | { | ||
1432 | int i; | ||
1433 | |||
1434 | for (i=0; i<sizeof(reason_names)/sizeof(char *); i++) | ||
1435 | if (strcmp(reason_names[i],s)==0) | ||
1436 | return reason_values[i]; | ||
1437 | return sr_unknown; | ||
1438 | } | ||
1439 | |||
1440 | const char *mi_reason_enum_to_str(enum mi_stop_reason r) | ||
1441 | { | ||
1442 | int i; | ||
1443 | |||
1444 | if (r==sr_unknown) | ||
1445 | return "Unknown (temp bkp?)"; | ||
1446 | for (i=0; i<sizeof(reason_values)/sizeof(char *); i++) | ||
1447 | if (reason_values[i]==r) | ||
1448 | return reason_expl[i]; | ||
1449 | return NULL; | ||
1450 | } | ||
1451 | |||
1452 | mi_stop *mi_get_stopped(mi_results *r) | ||
1453 | { | ||
1454 | mi_stop *res=mi_alloc_stop(); | ||
1455 | |||
1456 | if (res) | ||
1457 | { | ||
1458 | while (r) | ||
1459 | { | ||
1460 | if (r->type==t_const) | ||
1461 | { | ||
1462 | if (strcmp(r->var,"reason")==0) | ||
1463 | res->reason=mi_reason_str_to_enum(r->v.cstr); | ||
1464 | else if (!res->have_thread_id && strcmp(r->var,"thread-id")==0) | ||
1465 | { | ||
1466 | res->have_thread_id=1; | ||
1467 | res->thread_id=atoi(r->v.cstr); | ||
1468 | } | ||
1469 | else if (!res->have_bkptno && strcmp(r->var,"bkptno")==0) | ||
1470 | { | ||
1471 | res->have_bkptno=1; | ||
1472 | res->bkptno=atoi(r->v.cstr); | ||
1473 | } | ||
1474 | else if (!res->have_bkptno && strcmp(r->var,"wpnum")==0) | ||
1475 | { | ||
1476 | res->have_wpno=1; | ||
1477 | res->wpno=atoi(r->v.cstr); | ||
1478 | } | ||
1479 | else if (strcmp(r->var,"gdb-result-var")==0) | ||
1480 | { | ||
1481 | res->gdb_result_var=r->v.cstr; | ||
1482 | r->v.cstr=NULL; | ||
1483 | } | ||
1484 | else if (strcmp(r->var,"return-value")==0) | ||
1485 | { | ||
1486 | res->return_value=r->v.cstr; | ||
1487 | r->v.cstr=NULL; | ||
1488 | } | ||
1489 | else if (strcmp(r->var,"signal-name")==0) | ||
1490 | { | ||
1491 | res->signal_name=r->v.cstr; | ||
1492 | r->v.cstr=NULL; | ||
1493 | } | ||
1494 | else if (strcmp(r->var,"signal-meaning")==0) | ||
1495 | { | ||
1496 | res->signal_meaning=r->v.cstr; | ||
1497 | r->v.cstr=NULL; | ||
1498 | } | ||
1499 | else if (!res->have_exit_code && strcmp(r->var,"exit-code")==0) | ||
1500 | { | ||
1501 | res->have_exit_code=1; | ||
1502 | res->exit_code=atoi(r->v.cstr); | ||
1503 | } | ||
1504 | } | ||
1505 | else // tuple or list | ||
1506 | { | ||
1507 | if (strcmp(r->var,"frame")==0) | ||
1508 | res->frame=mi_parse_frame(r->v.rs); | ||
1509 | else if (!res->wp && strcmp(r->var,"wpt")==0) | ||
1510 | res->wp=mi_get_wp(r->v.rs,wm_write); | ||
1511 | else if (!res->wp && strcmp(r->var,"hw-rwpt")==0) | ||
1512 | res->wp=mi_get_wp(r->v.rs,wm_read); | ||
1513 | else if (!res->wp && strcmp(r->var,"hw-awpt")==0) | ||
1514 | res->wp=mi_get_wp(r->v.rs,wm_rw); | ||
1515 | else if (!(res->wp_old || res->wp_val) && strcmp(r->var,"value")==0) | ||
1516 | { | ||
1517 | mi_results *p=r->v.rs; | ||
1518 | while (p) | ||
1519 | { | ||
1520 | if (strcmp(p->var,"value")==0 || strcmp(p->var,"new")==0) | ||
1521 | { | ||
1522 | res->wp_val=p->v.cstr; | ||
1523 | p->v.cstr=NULL; | ||
1524 | } | ||
1525 | else if (strcmp(p->var,"old")==0) | ||
1526 | { | ||
1527 | res->wp_old=p->v.cstr; | ||
1528 | p->v.cstr=NULL; | ||
1529 | } | ||
1530 | p=p->next; | ||
1531 | } | ||
1532 | } | ||
1533 | } | ||
1534 | r=r->next; | ||
1535 | } | ||
1536 | } | ||
1537 | return res; | ||
1538 | } | ||
1539 | |||
1540 | mi_stop *mi_res_stop(mi_h *h) | ||
1541 | { | ||
1542 | mi_output *o=mi_retire_response(h); | ||
1543 | mi_stop *stop=NULL; | ||
1544 | |||
1545 | if (o) | ||
1546 | { | ||
1547 | mi_output *sr=mi_get_stop_record(o); | ||
1548 | if (sr) | ||
1549 | stop=mi_get_stopped(sr->c); | ||
1550 | } | ||
1551 | mi_free_output(o); | ||
1552 | |||
1553 | return stop; | ||
1554 | } | ||
1555 | |||
1556 | int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, | ||
1557 | unsigned long *addr) | ||
1558 | { | ||
1559 | char *end; | ||
1560 | mi_results *res=mi_res_done_var(h,"memory"), *r; | ||
1561 | int ok=0; | ||
1562 | |||
1563 | *na=0; | ||
1564 | r=res; | ||
1565 | if (r && r->type==t_list && ws==1) | ||
1566 | { | ||
1567 | r=r->v.rs; | ||
1568 | if (r->type!=t_tuple) | ||
1569 | { | ||
1570 | mi_free_results(res); | ||
1571 | return 0; | ||
1572 | } | ||
1573 | r=r->v.rs; | ||
1574 | while (r) | ||
1575 | { | ||
1576 | if (r->type==t_list && strcmp(r->var,"data")==0) | ||
1577 | { | ||
1578 | mi_results *data=r->v.rs; | ||
1579 | ok++; | ||
1580 | if (data && data->type==t_const && | ||
1581 | strcmp(data->v.cstr,"N/A")==0) | ||
1582 | *na=1; | ||
1583 | else | ||
1584 | while (data) | ||
1585 | { | ||
1586 | if (data->type==t_const) | ||
1587 | *(dest++)=strtol(data->v.cstr,&end,0); | ||
1588 | data=data->next; | ||
1589 | } | ||
1590 | } | ||
1591 | else if (r->type==t_const && strcmp(r->var,"addr")==0) | ||
1592 | { | ||
1593 | ok++; | ||
1594 | if (addr) | ||
1595 | *addr=strtoul(r->v.cstr,&end,0); | ||
1596 | } | ||
1597 | r=r->next; | ||
1598 | } | ||
1599 | |||
1600 | } | ||
1601 | mi_free_results(res); | ||
1602 | return ok==2; | ||
1603 | } | ||
1604 | |||
1605 | mi_asm_insn *mi_parse_insn(mi_results *c) | ||
1606 | { | ||
1607 | mi_asm_insn *res=NULL, *cur=NULL; | ||
1608 | mi_results *sub; | ||
1609 | char *end; | ||
1610 | |||
1611 | while (c) | ||
1612 | { | ||
1613 | if (c->type==t_tuple) | ||
1614 | { | ||
1615 | if (!res) | ||
1616 | res=cur=mi_alloc_asm_insn(); | ||
1617 | else | ||
1618 | { | ||
1619 | cur->next=mi_alloc_asm_insn(); | ||
1620 | cur=cur->next; | ||
1621 | } | ||
1622 | if (!cur) | ||
1623 | { | ||
1624 | mi_free_asm_insn(res); | ||
1625 | return NULL; | ||
1626 | } | ||
1627 | sub=c->v.rs; | ||
1628 | while (sub) | ||
1629 | { | ||
1630 | if (sub->type==t_const) | ||
1631 | { | ||
1632 | if (strcmp(sub->var,"address")==0) | ||
1633 | cur->addr=(void *)strtoul(sub->v.cstr,&end,0); | ||
1634 | else if (strcmp(sub->var,"func-name")==0) | ||
1635 | { | ||
1636 | cur->func=sub->v.cstr; | ||
1637 | sub->v.cstr=NULL; | ||
1638 | } | ||
1639 | else if (strcmp(sub->var,"offset")==0) | ||
1640 | cur->offset=atoi(sub->v.cstr); | ||
1641 | else if (strcmp(sub->var,"inst")==0) | ||
1642 | { | ||
1643 | cur->inst=sub->v.cstr; | ||
1644 | sub->v.cstr=NULL; | ||
1645 | } | ||
1646 | } | ||
1647 | sub=sub->next; | ||
1648 | } | ||
1649 | } | ||
1650 | c=c->next; | ||
1651 | } | ||
1652 | return res; | ||
1653 | } | ||
1654 | |||
1655 | mi_asm_insns *mi_parse_insns(mi_results *c) | ||
1656 | { | ||
1657 | mi_asm_insns *res=NULL, *cur=NULL; | ||
1658 | mi_results *sub; | ||
1659 | |||
1660 | while (c) | ||
1661 | { | ||
1662 | if (c->var) | ||
1663 | { | ||
1664 | if (strcmp(c->var,"src_and_asm_line")==0 && c->type==t_tuple) | ||
1665 | { | ||
1666 | if (!res) | ||
1667 | res=cur=mi_alloc_asm_insns(); | ||
1668 | else | ||
1669 | { | ||
1670 | cur->next=mi_alloc_asm_insns(); | ||
1671 | cur=cur->next; | ||
1672 | } | ||
1673 | if (!cur) | ||
1674 | { | ||
1675 | mi_free_asm_insns(res); | ||
1676 | return NULL; | ||
1677 | } | ||
1678 | sub=c->v.rs; | ||
1679 | while (sub) | ||
1680 | { | ||
1681 | if (sub->var) | ||
1682 | { | ||
1683 | if (sub->type==t_const) | ||
1684 | { | ||
1685 | if (strcmp(sub->var,"line")==0) | ||
1686 | cur->line=atoi(sub->v.cstr); | ||
1687 | else if (strcmp(sub->var,"file")==0) | ||
1688 | { | ||
1689 | cur->file=sub->v.cstr; | ||
1690 | sub->v.cstr=NULL; | ||
1691 | } | ||
1692 | } | ||
1693 | else if (sub->type==t_list) | ||
1694 | { | ||
1695 | if (strcmp(sub->var,"line_asm_insn")==0) | ||
1696 | cur->ins=mi_parse_insn(sub->v.rs); | ||
1697 | } | ||
1698 | } | ||
1699 | sub=sub->next; | ||
1700 | } | ||
1701 | } | ||
1702 | } | ||
1703 | else | ||
1704 | {/* No source line, just instructions */ | ||
1705 | res=mi_alloc_asm_insns(); | ||
1706 | res->ins=mi_parse_insn(c); | ||
1707 | break; | ||
1708 | } | ||
1709 | c=c->next; | ||
1710 | } | ||
1711 | return res; | ||
1712 | } | ||
1713 | |||
1714 | |||
1715 | mi_asm_insns *mi_get_asm_insns(mi_h *h) | ||
1716 | { | ||
1717 | mi_results *r=mi_res_done_var(h,"asm_insns"); | ||
1718 | mi_asm_insns *f=NULL; | ||
1719 | |||
1720 | if (r && r->type==t_list) | ||
1721 | f=mi_parse_insns(r->v.rs); | ||
1722 | mi_free_results(r); | ||
1723 | return f; | ||
1724 | } | ||
1725 | |||
1726 | mi_chg_reg *mi_parse_list_regs(mi_results *r, int *how_many) | ||
1727 | { | ||
1728 | mi_results *c=r; | ||
1729 | int cregs=0; | ||
1730 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1731 | |||
1732 | /* Create the list. */ | ||
1733 | while (c) | ||
1734 | { | ||
1735 | if (c->type==t_const && !c->var) | ||
1736 | { | ||
1737 | if (first) | ||
1738 | cur=cur->next=mi_alloc_chg_reg(); | ||
1739 | else | ||
1740 | first=cur=mi_alloc_chg_reg(); | ||
1741 | cur->name=c->v.cstr; | ||
1742 | cur->reg=cregs++; | ||
1743 | c->v.cstr=NULL; | ||
1744 | } | ||
1745 | c=c->next; | ||
1746 | } | ||
1747 | if (how_many) | ||
1748 | *how_many=cregs; | ||
1749 | |||
1750 | return first; | ||
1751 | } | ||
1752 | |||
1753 | mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many) | ||
1754 | { | ||
1755 | mi_results *r=mi_res_done_var(h,"register-names"); | ||
1756 | mi_chg_reg *l=NULL; | ||
1757 | |||
1758 | if (r && r->type==t_list) | ||
1759 | l=mi_parse_list_regs(r->v.rs,how_many); | ||
1760 | mi_free_results(r); | ||
1761 | return l; | ||
1762 | } | ||
1763 | |||
1764 | mi_chg_reg *mi_parse_list_changed_regs(mi_results *r) | ||
1765 | { | ||
1766 | mi_results *c=r; | ||
1767 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1768 | |||
1769 | /* Create the list. */ | ||
1770 | while (c) | ||
1771 | { | ||
1772 | if (c->type==t_const && !c->var) | ||
1773 | { | ||
1774 | if (first) | ||
1775 | cur=cur->next=mi_alloc_chg_reg(); | ||
1776 | else | ||
1777 | first=cur=mi_alloc_chg_reg(); | ||
1778 | cur->reg=atoi(c->v.cstr); | ||
1779 | } | ||
1780 | c=c->next; | ||
1781 | } | ||
1782 | |||
1783 | return first; | ||
1784 | } | ||
1785 | |||
1786 | mi_chg_reg *mi_get_list_changed_regs(mi_h *h) | ||
1787 | { | ||
1788 | mi_results *r=mi_res_done_var(h,"changed-registers"); | ||
1789 | mi_chg_reg *changed=NULL; | ||
1790 | |||
1791 | if (r && r->type==t_list) | ||
1792 | changed=mi_parse_list_changed_regs(r->v.rs); | ||
1793 | mi_free_results(r); | ||
1794 | return changed; | ||
1795 | } | ||
1796 | |||
1797 | int mi_parse_reg_values(mi_results *r, mi_chg_reg *l) | ||
1798 | { | ||
1799 | mi_results *c; | ||
1800 | |||
1801 | while (r && l) | ||
1802 | { | ||
1803 | if (r->type==t_tuple && !r->var) | ||
1804 | { | ||
1805 | c=r->v.rs; | ||
1806 | while (c) | ||
1807 | { | ||
1808 | if (c->type==t_const && c->var) | ||
1809 | { | ||
1810 | if (strcmp(c->var,"number")==0) | ||
1811 | { | ||
1812 | if (atoi(c->v.cstr)!=l->reg) | ||
1813 | { | ||
1814 | mi_error=MI_PARSER; | ||
1815 | return 0; | ||
1816 | } | ||
1817 | } | ||
1818 | else if (strcmp(c->var,"value")==0) | ||
1819 | { | ||
1820 | l->val=c->v.cstr; | ||
1821 | c->v.cstr=NULL; | ||
1822 | } | ||
1823 | } | ||
1824 | c=c->next; | ||
1825 | } | ||
1826 | } | ||
1827 | r=r->next; | ||
1828 | l=l->next; | ||
1829 | } | ||
1830 | |||
1831 | return !l && !r; | ||
1832 | } | ||
1833 | |||
1834 | int mi_get_reg_values(mi_h *h, mi_chg_reg *l) | ||
1835 | { | ||
1836 | mi_results *r=mi_res_done_var(h,"register-values"); | ||
1837 | int ok=0; | ||
1838 | |||
1839 | if (r && r->type==t_list) | ||
1840 | ok=mi_parse_reg_values(r->v.rs,l); | ||
1841 | mi_free_results(r); | ||
1842 | return ok; | ||
1843 | } | ||
1844 | |||
1845 | int mi_parse_list_regs_l(mi_results *r, mi_chg_reg *l) | ||
1846 | { | ||
1847 | while (r && l) | ||
1848 | { | ||
1849 | if (r->type==t_const && !r->var) | ||
1850 | { | ||
1851 | free(l->name); | ||
1852 | l->name=r->v.cstr; | ||
1853 | r->v.cstr=NULL; | ||
1854 | l=l->next; | ||
1855 | } | ||
1856 | r=r->next; | ||
1857 | } | ||
1858 | |||
1859 | return !l && !r; | ||
1860 | } | ||
1861 | |||
1862 | int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l) | ||
1863 | { | ||
1864 | mi_results *r=mi_res_done_var(h,"register-names"); | ||
1865 | int ok=0; | ||
1866 | |||
1867 | if (r && r->type==t_list) | ||
1868 | ok=mi_parse_list_regs_l(r->v.rs,l); | ||
1869 | mi_free_results(r); | ||
1870 | return ok; | ||
1871 | } | ||
1872 | |||
1873 | mi_chg_reg *mi_parse_reg_values_l(mi_results *r, int *how_many) | ||
1874 | { | ||
1875 | mi_results *c; | ||
1876 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1877 | *how_many=0; | ||
1878 | |||
1879 | while (r) | ||
1880 | { | ||
1881 | if (r->type==t_tuple && !r->var) | ||
1882 | { | ||
1883 | c=r->v.rs; | ||
1884 | if (first) | ||
1885 | cur=cur->next=mi_alloc_chg_reg(); | ||
1886 | else | ||
1887 | first=cur=mi_alloc_chg_reg(); | ||
1888 | while (c) | ||
1889 | { | ||
1890 | if (c->type==t_const && c->var) | ||
1891 | { | ||
1892 | if (strcmp(c->var,"number")==0) | ||
1893 | { | ||
1894 | cur->reg=atoi(c->v.cstr); | ||
1895 | (*how_many)++; | ||
1896 | } | ||
1897 | else if (strcmp(c->var,"value")==0) | ||
1898 | { | ||
1899 | cur->val=c->v.cstr; | ||
1900 | c->v.cstr=NULL; | ||
1901 | } | ||
1902 | } | ||
1903 | c=c->next; | ||
1904 | } | ||
1905 | } | ||
1906 | r=r->next; | ||
1907 | } | ||
1908 | |||
1909 | return first; | ||
1910 | } | ||
1911 | |||
1912 | mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many) | ||
1913 | { | ||
1914 | mi_results *r=mi_res_done_var(h,"register-values"); | ||
1915 | mi_chg_reg *rgs=NULL; | ||
1916 | |||
1917 | if (r && r->type==t_list) | ||
1918 | rgs=mi_parse_reg_values_l(r->v.rs,how_many); | ||
1919 | mi_free_results(r); | ||
1920 | return rgs; | ||
1921 | } | ||
1922 | |||
1923 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Program control. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Program Control" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | |||
13 | -exec-abort N.A. (*) (kill, but with non-interactive options) | ||
14 | -exec-arguments Yes | ||
15 | -exec-continue Yes ASYNC | ||
16 | -exec-finish Yes ASYNC | ||
17 | -exec-interrupt Yes ASYNC | ||
18 | -exec-next Yes ASYNC | ||
19 | -exec-next-instruction Yes ASYNC | ||
20 | -exec-return Yes | ||
21 | -exec-run Yes ASYNC | ||
22 | -exec-show-arguments N.A. (show args) see gmi_stack_info_frame | ||
23 | -exec-step Yes ASYNC | ||
24 | -exec-step-instruction Yes ASYNC | ||
25 | -exec-until Yes ASYNC | ||
26 | -file-exec-and-symbols Yes | ||
27 | -file-exec-file No | ||
28 | -file-list-exec-sections N.A. (info file) | ||
29 | -file-list-exec-source-files N.A. | ||
30 | -file-list-shared-libraries N.A. | ||
31 | -file-list-symbol-files N.A. | ||
32 | -file-symbol-file Yes | ||
33 | @</pre> | ||
34 | |||
35 | (*) gmi_exec_kill implements it, but you should ensure that | ||
36 | gmi_gdb_set("confirm","off") was called.@p | ||
37 | |||
38 | GDB Bug workaround for -file-exec-and-symbols and -file-symbol-file: This | ||
39 | is complex, but a real bug. When you set a breakpoint you never know the | ||
40 | name of the file as it appears in the debug info. So you can be specifying | ||
41 | an absolute file name or a relative file name. The reference point could be | ||
42 | different than the one used in the debug info. To solve all the combinations | ||
43 | gdb does a search trying various combinations. GDB isn't very smart so you | ||
44 | must at least specify the working directory and the directory where the | ||
45 | binary is located to get a good chance (+ user options to solve the rest). | ||
46 | Once you did it gdb can find the file by doing transformations to the | ||
47 | "canonical" filename. This search works OK for already loaded symtabs | ||
48 | (symbol tables), but it have a bug when the search is done for psymtabs | ||
49 | (partial symtabs). The bug is in the use of source_full_path_of (source.c). | ||
50 | This function calls openp indicating try_cwd_first. It makes the search file | ||
51 | if the psymtab file name have at least one dirseparator. It means that | ||
52 | psymtabs for files compiled with relative paths will fail. The search for | ||
53 | symtabs uses symtab_to_filename, it calls open_source_file which finally | ||
54 | calls openp without try_cwd_first.@* | ||
55 | To workaround this bug we must ensure gdb loads *all* the symtabs to memory. | ||
56 | And here comes another problem -file-exec-and-symbols doesn't support it | ||
57 | according to docs. In real life that's a wrapper for "file", but as nobody | ||
58 | can say it won't change we must use the CLI command. | ||
59 | |||
60 | ***************************************************************************/ | ||
61 | |||
62 | #include <signal.h> | ||
63 | #include "mi_gdb.h" | ||
64 | |||
65 | /* Low level versions. */ | ||
66 | |||
67 | void mi_file_exec_and_symbols(mi_h *h, const char *file) | ||
68 | { | ||
69 | if (mi_get_workaround(MI_PSYM_SEARCH)) | ||
70 | mi_send(h,"file %s -readnow\n",file); | ||
71 | else | ||
72 | mi_send(h,"-file-exec-and-symbols %s\n",file); | ||
73 | } | ||
74 | |||
75 | void mi_exec_arguments(mi_h *h, const char *args) | ||
76 | { | ||
77 | mi_send(h,"-exec-arguments %s\n",args); | ||
78 | } | ||
79 | |||
80 | void mi_exec_run(mi_h *h) | ||
81 | { | ||
82 | mi_send(h,"-exec-run\n"); | ||
83 | } | ||
84 | |||
85 | void mi_exec_continue(mi_h *h) | ||
86 | { | ||
87 | mi_send(h,"-exec-continue\n"); | ||
88 | } | ||
89 | |||
90 | void mi_target_terminal(mi_h *h, const char *tty_name) | ||
91 | { | ||
92 | mi_send(h,"tty %s\n",tty_name); | ||
93 | } | ||
94 | |||
95 | void mi_file_symbol_file(mi_h *h, const char *file) | ||
96 | { | ||
97 | if (mi_get_workaround(MI_PSYM_SEARCH)) | ||
98 | mi_send(h,"symbol-file %s -readnow\n",file); | ||
99 | else | ||
100 | mi_send(h,"-file-symbol-file %s\n",file); | ||
101 | } | ||
102 | |||
103 | void mi_exec_finish(mi_h *h) | ||
104 | { | ||
105 | mi_send(h,"-exec-finish\n"); | ||
106 | } | ||
107 | |||
108 | void mi_exec_interrupt(mi_h *h) | ||
109 | { | ||
110 | mi_send(h,"-exec-interrupt\n"); | ||
111 | } | ||
112 | |||
113 | void mi_exec_next(mi_h *h, int count) | ||
114 | { | ||
115 | if (count>1) | ||
116 | mi_send(h,"-exec-next %d\n",count); | ||
117 | else | ||
118 | mi_send(h,"-exec-next\n"); | ||
119 | } | ||
120 | |||
121 | void mi_exec_next_instruction(mi_h *h) | ||
122 | { | ||
123 | mi_send(h,"-exec-next-instruction\n"); | ||
124 | } | ||
125 | |||
126 | void mi_exec_step(mi_h *h, int count) | ||
127 | { | ||
128 | if (count>1) | ||
129 | mi_send(h,"-exec-step %d\n",count); | ||
130 | else | ||
131 | mi_send(h,"-exec-step\n"); | ||
132 | } | ||
133 | |||
134 | void mi_exec_step_instruction(mi_h *h) | ||
135 | { | ||
136 | mi_send(h,"-exec-step-instruction\n"); | ||
137 | } | ||
138 | |||
139 | void mi_exec_until(mi_h *h, const char *file, int line) | ||
140 | { | ||
141 | if (!file) | ||
142 | mi_send(h,"-exec-until\n"); | ||
143 | else | ||
144 | mi_send(h,"-exec-until %s:%d\n",file,line); | ||
145 | } | ||
146 | |||
147 | void mi_exec_until_addr(mi_h *h, void *addr) | ||
148 | { | ||
149 | mi_send(h,"-exec-until *%p\n",addr); | ||
150 | } | ||
151 | |||
152 | void mi_exec_return(mi_h *h) | ||
153 | { | ||
154 | mi_send(h,"-exec-return\n"); | ||
155 | } | ||
156 | |||
157 | void mi_exec_kill(mi_h *h) | ||
158 | { | ||
159 | mi_send(h,"kill\n"); | ||
160 | } | ||
161 | |||
162 | /* High level versions. */ | ||
163 | |||
164 | /**[txh]******************************************************************** | ||
165 | |||
166 | Description: | ||
167 | Specify the executable and arguments for local debug. | ||
168 | |||
169 | Command: -file-exec-and-symbols + -exec-arguments | ||
170 | Return: !=0 OK | ||
171 | |||
172 | ***************************************************************************/ | ||
173 | |||
174 | int gmi_set_exec(mi_h *h, const char *file, const char *args) | ||
175 | { | ||
176 | mi_file_exec_and_symbols(h,file); | ||
177 | if (!mi_res_simple_done(h)) | ||
178 | return 0; | ||
179 | if (!args) | ||
180 | return 1; | ||
181 | mi_exec_arguments(h,args); | ||
182 | return mi_res_simple_done(h); | ||
183 | } | ||
184 | |||
185 | /**[txh]******************************************************************** | ||
186 | |||
187 | Description: | ||
188 | Start running the executable. Remote sessions starts running. | ||
189 | |||
190 | Command: -exec-run | ||
191 | Return: !=0 OK | ||
192 | |||
193 | ***************************************************************************/ | ||
194 | |||
195 | int gmi_exec_run(mi_h *h) | ||
196 | { | ||
197 | mi_exec_run(h); | ||
198 | return mi_res_simple_running(h); | ||
199 | } | ||
200 | |||
201 | /**[txh]******************************************************************** | ||
202 | |||
203 | Description: | ||
204 | Continue the execution after a "stop". | ||
205 | |||
206 | Command: -exec-continue | ||
207 | Return: !=0 OK | ||
208 | |||
209 | ***************************************************************************/ | ||
210 | |||
211 | int gmi_exec_continue(mi_h *h) | ||
212 | { | ||
213 | mi_exec_continue(h); | ||
214 | return mi_res_simple_running(h); | ||
215 | } | ||
216 | |||
217 | /**[txh]******************************************************************** | ||
218 | |||
219 | Description: | ||
220 | Indicate which terminal will use the target program. For local sessions. | ||
221 | |||
222 | Command: tty | ||
223 | Return: !=0 OK | ||
224 | Example: | ||
225 | |||
226 | ***************************************************************************/ | ||
227 | |||
228 | int gmi_target_terminal(mi_h *h, const char *tty_name) | ||
229 | { | ||
230 | mi_target_terminal(h,tty_name); | ||
231 | return mi_res_simple_done(h); | ||
232 | } | ||
233 | |||
234 | /**[txh]******************************************************************** | ||
235 | |||
236 | Description: | ||
237 | Specify what's the local copy that have debug info. For remote sessions. | ||
238 | |||
239 | Command: -file-symbol-file | ||
240 | Return: !=0 OK | ||
241 | |||
242 | ***************************************************************************/ | ||
243 | |||
244 | int gmi_file_symbol_file(mi_h *h, const char *file) | ||
245 | { | ||
246 | mi_file_symbol_file(h,file); | ||
247 | return mi_res_simple_done(h); | ||
248 | } | ||
249 | |||
250 | /**[txh]******************************************************************** | ||
251 | |||
252 | Description: | ||
253 | Continue until function return, the return value is included in the async | ||
254 | response. | ||
255 | |||
256 | Command: -exec-finish | ||
257 | Return: !=0 OK. | ||
258 | |||
259 | ***************************************************************************/ | ||
260 | |||
261 | int gmi_exec_finish(mi_h *h) | ||
262 | { | ||
263 | mi_exec_finish(h); | ||
264 | return mi_res_simple_running(h); | ||
265 | } | ||
266 | |||
267 | /**[txh]******************************************************************** | ||
268 | |||
269 | Description: | ||
270 | Stop the program using SIGINT. The corresponding command should be | ||
271 | -exec-interrupt but not even gdb 6.1.1 can do it because the "async" mode | ||
272 | isn't really working. | ||
273 | |||
274 | Command: -exec-interrupt [replacement] | ||
275 | Return: Always 1 | ||
276 | Example: | ||
277 | |||
278 | ***************************************************************************/ | ||
279 | |||
280 | int gmi_exec_interrupt(mi_h *h) | ||
281 | { | ||
282 | // **** IMPORTANT!!! **** Not even gdb 6.1.1 can do it because the "async" | ||
283 | // mode isn't really working. | ||
284 | //mi_exec_interrupt(h); | ||
285 | //return mi_res_simple_running(h); | ||
286 | |||
287 | kill(h->pid,SIGINT); | ||
288 | return 1; // How can I know? | ||
289 | } | ||
290 | |||
291 | /**[txh]******************************************************************** | ||
292 | |||
293 | Description: | ||
294 | Next line of code. | ||
295 | |||
296 | Command: -exec-next | ||
297 | Return: !=0 OK | ||
298 | |||
299 | ***************************************************************************/ | ||
300 | |||
301 | int gmi_exec_next(mi_h *h) | ||
302 | { | ||
303 | mi_exec_next(h,1); | ||
304 | return mi_res_simple_running(h); | ||
305 | } | ||
306 | |||
307 | /**[txh]******************************************************************** | ||
308 | |||
309 | Description: | ||
310 | Skip count lines of code. | ||
311 | |||
312 | Command: -exec-next count | ||
313 | Return: !=0 OK | ||
314 | |||
315 | ***************************************************************************/ | ||
316 | |||
317 | int gmi_exec_next_cnt(mi_h *h, int count) | ||
318 | { | ||
319 | mi_exec_next(h,count); | ||
320 | return mi_res_simple_running(h); | ||
321 | } | ||
322 | |||
323 | /**[txh]******************************************************************** | ||
324 | |||
325 | Description: | ||
326 | Next line of assembler code. | ||
327 | |||
328 | Command: -exec-next-instruction | ||
329 | Return: !=0 OK | ||
330 | |||
331 | ***************************************************************************/ | ||
332 | |||
333 | int gmi_exec_next_instruction(mi_h *h) | ||
334 | { | ||
335 | mi_exec_next_instruction(h); | ||
336 | return mi_res_simple_running(h); | ||
337 | } | ||
338 | |||
339 | /**[txh]******************************************************************** | ||
340 | |||
341 | Description: | ||
342 | Next line of code. Get inside functions. | ||
343 | |||
344 | Command: -exec-step | ||
345 | Return: !=0 OK | ||
346 | |||
347 | ***************************************************************************/ | ||
348 | |||
349 | int gmi_exec_step(mi_h *h) | ||
350 | { | ||
351 | mi_exec_step(h,1); | ||
352 | return mi_res_simple_running(h); | ||
353 | } | ||
354 | |||
355 | /**[txh]******************************************************************** | ||
356 | |||
357 | Description: | ||
358 | Next count lines of code. Get inside functions. | ||
359 | |||
360 | Command: -exec-step count | ||
361 | Return: !=0 OK | ||
362 | |||
363 | ***************************************************************************/ | ||
364 | |||
365 | int gmi_exec_step_cnt(mi_h *h, int count) | ||
366 | { | ||
367 | mi_exec_step(h,count); | ||
368 | return mi_res_simple_running(h); | ||
369 | } | ||
370 | |||
371 | /**[txh]******************************************************************** | ||
372 | |||
373 | Description: | ||
374 | Next line of assembler code. Get inside calls. | ||
375 | |||
376 | Command: -exec-step-instruction | ||
377 | Return: !=0 OK | ||
378 | |||
379 | ***************************************************************************/ | ||
380 | |||
381 | int gmi_exec_step_instruction(mi_h *h) | ||
382 | { | ||
383 | mi_exec_step_instruction(h); | ||
384 | return mi_res_simple_running(h); | ||
385 | } | ||
386 | |||
387 | /**[txh]******************************************************************** | ||
388 | |||
389 | Description: | ||
390 | Execute until location is reached. If file is NULL then is until next | ||
391 | line. | ||
392 | |||
393 | Command: -exec-until | ||
394 | Return: !=0 OK | ||
395 | |||
396 | ***************************************************************************/ | ||
397 | |||
398 | int gmi_exec_until(mi_h *h, const char *file, int line) | ||
399 | { | ||
400 | mi_exec_until(h,file,line); | ||
401 | return mi_res_simple_running(h); | ||
402 | } | ||
403 | |||
404 | /**[txh]******************************************************************** | ||
405 | |||
406 | Description: | ||
407 | Execute until location is reached. | ||
408 | |||
409 | Command: -exec-until (using *address) | ||
410 | Return: !=0 OK | ||
411 | |||
412 | ***************************************************************************/ | ||
413 | |||
414 | int gmi_exec_until_addr(mi_h *h, void *addr) | ||
415 | { | ||
416 | mi_exec_until_addr(h,addr); | ||
417 | return mi_res_simple_running(h); | ||
418 | } | ||
419 | |||
420 | /**[txh]******************************************************************** | ||
421 | |||
422 | Description: | ||
423 | Return to previous frame inmediatly. | ||
424 | |||
425 | Command: -exec-return | ||
426 | Return: A pointer to a new mi_frames structure indicating the current | ||
427 | location. NULL on error. | ||
428 | |||
429 | ***************************************************************************/ | ||
430 | |||
431 | mi_frames *gmi_exec_return(mi_h *h) | ||
432 | { | ||
433 | mi_exec_return(h); | ||
434 | return mi_res_frame(h); | ||
435 | } | ||
436 | |||
437 | /**[txh]******************************************************************** | ||
438 | |||
439 | Description: | ||
440 | Just kill the program. That's what -exec-abort should do, but it isn't | ||
441 | implemented by gdb. This implementation only works if the interactive mode | ||
442 | is disabled (gmi_gdb_set("confirm","off")). | ||
443 | |||
444 | Command: -exec-abort [using kill] | ||
445 | Return: !=0 OK | ||
446 | |||
447 | ***************************************************************************/ | ||
448 | |||
449 | int gmi_exec_kill(mi_h *h) | ||
450 | { | ||
451 | mi_exec_kill(h); | ||
452 | return mi_res_simple_done(h); | ||
453 | } | ||
454 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Stack manipulation. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Stack Manipulation" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | |||
13 | -stack-info-frame Yes, implemented as "frame" | ||
14 | -stack-info-depth Yes | ||
15 | -stack-list-arguments Yes | ||
16 | -stack-list-frames Yes | ||
17 | -stack-list-locals Yes | ||
18 | -stack-select-frame Yes | ||
19 | @</pre> | ||
20 | |||
21 | ***************************************************************************/ | ||
22 | |||
23 | #include "mi_gdb.h" | ||
24 | |||
25 | /* Low level versions. */ | ||
26 | |||
27 | void mi_stack_list_frames(mi_h *h, int from, int to) | ||
28 | { | ||
29 | if (from<0) | ||
30 | mi_send(h,"-stack-list-frames\n"); | ||
31 | else | ||
32 | mi_send(h,"-stack-list-frames %d %d\n",from,to); | ||
33 | } | ||
34 | |||
35 | void mi_stack_list_arguments(mi_h *h, int show, int from, int to) | ||
36 | { | ||
37 | if (from<0) | ||
38 | mi_send(h,"-stack-list-arguments %d\n",show); | ||
39 | else | ||
40 | mi_send(h,"-stack-list-arguments %d %d %d\n",show,from,to); | ||
41 | } | ||
42 | |||
43 | void mi_stack_info_frame(mi_h *h) | ||
44 | { | ||
45 | mi_send(h,"frame\n"); | ||
46 | } | ||
47 | |||
48 | void mi_stack_info_depth(mi_h *h, int depth) | ||
49 | { | ||
50 | if (depth<0) | ||
51 | mi_send(h,"-stack-info-depth\n"); | ||
52 | else | ||
53 | mi_send(h,"-stack-info-depth %d\n",depth); | ||
54 | } | ||
55 | |||
56 | void mi_stack_select_frame(mi_h *h, int framenum) | ||
57 | { | ||
58 | mi_send(h,"-stack-select-frame %d\n",framenum); | ||
59 | } | ||
60 | |||
61 | void mi_stack_list_locals(mi_h *h, int show) | ||
62 | { | ||
63 | mi_send(h,"-stack-list-locals %d\n",show); | ||
64 | } | ||
65 | |||
66 | /* High level versions. */ | ||
67 | |||
68 | /**[txh]******************************************************************** | ||
69 | |||
70 | Description: | ||
71 | List of frames. Arguments aren't filled. | ||
72 | |||
73 | Command: -stack-list-frames | ||
74 | Return: A new list of mi_frames or NULL on error. | ||
75 | |||
76 | ***************************************************************************/ | ||
77 | |||
78 | mi_frames *gmi_stack_list_frames(mi_h *h) | ||
79 | { | ||
80 | mi_stack_list_frames(h,-1,-1); | ||
81 | return mi_res_frames_array(h,"stack"); | ||
82 | } | ||
83 | |||
84 | /**[txh]******************************************************************** | ||
85 | |||
86 | Description: | ||
87 | List of frames. Arguments aren't filled. Only the frames in the @var{from} | ||
88 | - @var{to} range are returned. | ||
89 | |||
90 | Command: -stack-list-frames | ||
91 | Return: A new list of mi_frames or NULL on error. | ||
92 | |||
93 | ***************************************************************************/ | ||
94 | |||
95 | mi_frames *gmi_stack_list_frames_r(mi_h *h, int from, int to) | ||
96 | { | ||
97 | mi_stack_list_frames(h,from,to); | ||
98 | return mi_res_frames_array(h,"stack"); | ||
99 | } | ||
100 | |||
101 | /**[txh]******************************************************************** | ||
102 | |||
103 | Description: | ||
104 | List arguments. Only @var{level} and @var{args} filled. | ||
105 | |||
106 | Command: -stack-list-arguments | ||
107 | Return: A new list of mi_frames or NULL on error. | ||
108 | |||
109 | ***************************************************************************/ | ||
110 | |||
111 | mi_frames *gmi_stack_list_arguments(mi_h *h, int show) | ||
112 | { | ||
113 | mi_stack_list_arguments(h,show,-1,-1); | ||
114 | return mi_res_frames_array(h,"stack-args"); | ||
115 | } | ||
116 | |||
117 | /**[txh]******************************************************************** | ||
118 | |||
119 | Description: | ||
120 | List arguments. Only @var{level} and @var{args} filled. Only for the | ||
121 | frames in the @var{from} - @var{to} range. | ||
122 | |||
123 | Command: -stack-list-arguments | ||
124 | Return: A new list of mi_frames or NULL on error. | ||
125 | |||
126 | ***************************************************************************/ | ||
127 | |||
128 | mi_frames *gmi_stack_list_arguments_r(mi_h *h, int show, int from, int to) | ||
129 | { | ||
130 | mi_stack_list_arguments(h,show,from,to); | ||
131 | return mi_res_frames_array(h,"stack-args"); | ||
132 | } | ||
133 | |||
134 | /**[txh]******************************************************************** | ||
135 | |||
136 | Description: | ||
137 | Information about the current frame, including args. | ||
138 | |||
139 | Command: -stack-info-frame [using frame] | ||
140 | Return: A new mi_frames or NULL on error. | ||
141 | |||
142 | ***************************************************************************/ | ||
143 | |||
144 | mi_frames *gmi_stack_info_frame(mi_h *h) | ||
145 | { | ||
146 | mi_stack_info_frame(h); | ||
147 | return mi_res_frame(h); | ||
148 | } | ||
149 | |||
150 | /**[txh]******************************************************************** | ||
151 | |||
152 | Description: | ||
153 | Stack info depth. | ||
154 | |||
155 | Command: -stack-info-depth | ||
156 | Return: The depth or -1 on error. | ||
157 | |||
158 | ***************************************************************************/ | ||
159 | |||
160 | int gmi_stack_info_depth(mi_h *h, int max_depth) | ||
161 | { | ||
162 | mi_results *r; | ||
163 | int ret=-1; | ||
164 | |||
165 | mi_stack_info_depth(h,max_depth); | ||
166 | r=mi_res_done_var(h,"depth"); | ||
167 | if (r && r->type==t_const) | ||
168 | { | ||
169 | ret=atoi(r->v.cstr); | ||
170 | mi_free_results(r); | ||
171 | } | ||
172 | return ret; | ||
173 | } | ||
174 | |||
175 | /**[txh]******************************************************************** | ||
176 | |||
177 | Description: | ||
178 | Set stack info depth. | ||
179 | |||
180 | Command: -stack-info-depth [no args] | ||
181 | Return: The depth or -1 on error. | ||
182 | Example: | ||
183 | |||
184 | ***************************************************************************/ | ||
185 | |||
186 | int gmi_stack_info_depth_get(mi_h *h) | ||
187 | { | ||
188 | return gmi_stack_info_depth(h,-1); | ||
189 | } | ||
190 | |||
191 | /**[txh]******************************************************************** | ||
192 | |||
193 | Description: | ||
194 | Change current frame. | ||
195 | |||
196 | Command: -stack-select-frame | ||
197 | Return: !=0 OK | ||
198 | |||
199 | ***************************************************************************/ | ||
200 | |||
201 | int gmi_stack_select_frame(mi_h *h, int framenum) | ||
202 | { | ||
203 | mi_stack_select_frame(h,framenum); | ||
204 | return mi_res_simple_done(h); | ||
205 | } | ||
206 | |||
207 | /**[txh]******************************************************************** | ||
208 | |||
209 | Description: | ||
210 | List of local vars. | ||
211 | |||
212 | Command: -stack-list-locals | ||
213 | Return: A new mi_results tree containing the variables or NULL on error. | ||
214 | |||
215 | ***************************************************************************/ | ||
216 | |||
217 | mi_results *gmi_stack_list_locals(mi_h *h, int show) | ||
218 | { | ||
219 | mi_stack_list_locals(h,show); | ||
220 | return mi_res_done_var(h,"locals"); | ||
221 | } | ||
222 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Symbol query. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Symbol Query" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | -symbol-info-address N.A. (info address, human readable) | ||
13 | -symbol-info-file N.A. | ||
14 | -symbol-info-function N.A. | ||
15 | -symbol-info-line N.A. (info line, human readable) | ||
16 | -symbol-info-symbol N.A. (info symbol, human readable) | ||
17 | -symbol-list-functions N.A. (info functions, human readable) | ||
18 | -symbol-list-types N.A. (info types, human readable) | ||
19 | -symbol-list-variables N.A. (info variables, human readable) | ||
20 | -symbol-list-lines No (gdb 6.x) | ||
21 | -symbol-locate N.A. | ||
22 | -symbol-type N.A. (ptype, human readable) | ||
23 | @</pre> | ||
24 | |||
25 | Note:@p | ||
26 | |||
27 | Only one is implemented and not in gdb 5.x.@p | ||
28 | |||
29 | ***************************************************************************/ | ||
30 | |||
31 | #include "mi_gdb.h" | ||
32 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2007 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Target manipulation. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Target Manipulation" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | -target-attach Yes (implemented using attach) | ||
12 | -target-compare-sections N.A. (compare-sections) | ||
13 | -target-detach Yes | ||
14 | -target-download Yes | ||
15 | -target-exec-status N.A. | ||
16 | -target-list-available-targets N.A. (help target) | ||
17 | -target-list-current-targets N.A. (info file among other things) | ||
18 | -target-list-parameters N.A. | ||
19 | -target-select Yes | ||
20 | @</pre> | ||
21 | |||
22 | ***************************************************************************/ | ||
23 | |||
24 | #include "mi_gdb.h" | ||
25 | |||
26 | /* Low level versions. */ | ||
27 | |||
28 | void mi_target_select(mi_h *h, const char *type, const char *params) | ||
29 | { | ||
30 | mi_send(h,"-target-select %s %s\n",type,params); | ||
31 | } | ||
32 | |||
33 | /* Note: -target-attach isn't currently implemented :-( (gdb 6.1.1) */ | ||
34 | void mi_target_attach(mi_h *h, pid_t pid) | ||
35 | { | ||
36 | mi_send(h,"attach %d\n",pid); | ||
37 | } | ||
38 | |||
39 | void mi_target_detach(mi_h *h) | ||
40 | { | ||
41 | mi_send(h,"-target-detach\n"); | ||
42 | } | ||
43 | |||
44 | void mi_target_download(mi_h *h) | ||
45 | { | ||
46 | mi_send(h,"-target-download\n"); | ||
47 | } | ||
48 | |||
49 | /* High level versions. */ | ||
50 | |||
51 | /**[txh]******************************************************************** | ||
52 | |||
53 | Description: | ||
54 | Connect to a remote gdbserver using the specified methode. | ||
55 | |||
56 | Command: -target-select | ||
57 | Return: !=0 OK | ||
58 | |||
59 | ***************************************************************************/ | ||
60 | |||
61 | int gmi_target_select(mi_h *h, const char *type, const char *params) | ||
62 | { | ||
63 | mi_target_select(h,type,params); | ||
64 | if (!mi_res_simple_connected(h)) | ||
65 | return 0; | ||
66 | mi_send_target_commands(h); | ||
67 | return 1; | ||
68 | } | ||
69 | |||
70 | /**[txh]******************************************************************** | ||
71 | |||
72 | Description: | ||
73 | Attach to an already running process. | ||
74 | |||
75 | Command: -target-attach [using attach] | ||
76 | Return: The frame of the current location, NULL on error. | ||
77 | |||
78 | ***************************************************************************/ | ||
79 | |||
80 | mi_frames *gmi_target_attach(mi_h *h, pid_t pid) | ||
81 | { | ||
82 | mi_target_attach(h,pid); | ||
83 | //return mi_res_simple_done(h); | ||
84 | return mi_res_frame(h); | ||
85 | } | ||
86 | |||
87 | /**[txh]******************************************************************** | ||
88 | |||
89 | Description: | ||
90 | Detach from an attached process. | ||
91 | |||
92 | Command: -target-detach | ||
93 | Return: !=0 OK | ||
94 | |||
95 | ***************************************************************************/ | ||
96 | |||
97 | int gmi_target_detach(mi_h *h) | ||
98 | { | ||
99 | mi_target_detach(h); | ||
100 | return mi_res_simple_done(h); | ||
101 | } | ||
102 | |||
103 | /**[txh]******************************************************************** | ||
104 | |||
105 | Description: | ||
106 | Loads the executable onto the remote target. | ||
107 | |||
108 | Command: -target-download | ||
109 | Return: !=0 OK | ||
110 | |||
111 | ***************************************************************************/ | ||
112 | |||
113 | int gmi_target_download(mi_h *h) | ||
114 | { | ||
115 | mi_target_download(h); | ||
116 | // TODO: this response have some data | ||
117 | return mi_res_simple_done(h); | ||
118 | } | ||
119 | |||
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 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | rm -rf /tmp/test-gnunetd-monkey/ | ||
4 | exe="./gnunet-monkey -c test_monkey_api_data.conf" | ||
5 | out=`mktemp /tmp/test-gnunet-monkey-logXXXXXXXX` | ||
6 | arm="gnunet-arm -c test_monkey_api_data.conf $DEBUG" | ||
7 | #DEBUG="-L DEBUG" | ||
8 | # ----------------------------------- | ||
9 | echo -n "Preparing: Starting service..." | ||
10 | |||
11 | $arm -s > /dev/null | ||
12 | sleep 1 | ||
13 | $arm -i monkey > /dev/null | ||
14 | sleep 1 | ||
15 | echo "DONE" | ||
16 | |||
17 | # ---------------------------------------------------------------------------------- | ||
18 | echo -n "TEST: Bad argument checking..." | ||
19 | |||
20 | if $exe -x 2> /dev/null; then | ||
21 | echo "FAIL: error running $exe" | ||
22 | $arm -e | ||
23 | exit 1 | ||
24 | fi | ||
25 | echo "PASS" | ||
26 | |||
27 | # ---------------------------------------------------------------------------------- | ||
28 | echo -n "TEST: Set value..." | ||
29 | |||
30 | if ! $exe $DEBUG -n test -s subsystem 42 ; then | ||
31 | echo "FAIL: error running $exe" | ||
32 | $arm -e | ||
33 | exit 1 | ||
34 | fi | ||
35 | echo "PASS" | ||
36 | |||
37 | # ---------------------------------------------------------------------------------- | ||
38 | echo -n "TEST: Set another value..." | ||
39 | |||
40 | if ! $exe $DEBUG -n other -s osystem 43 ; then | ||
41 | echo "FAIL: error running $exe" | ||
42 | $arm -e | ||
43 | exit 1 | ||
44 | fi | ||
45 | echo "PASS" | ||
46 | |||
47 | # ---------------------------------------------------------------------------------- | ||
48 | echo -n "TEST: viewing all stats..." | ||
49 | |||
50 | if ! $exe $DEBUG > $out; then | ||
51 | echo "FAIL: error running $exe" | ||
52 | $arm -e | ||
53 | exit 1 | ||
54 | fi | ||
55 | LINES=`cat $out | wc -l` | ||
56 | if test $LINES -ne 2; then | ||
57 | echo "FAIL: unexpected output" | ||
58 | $arm -e | ||
59 | exit 1 | ||
60 | fi | ||
61 | echo "PASS" | ||
62 | |||
63 | # ---------------------------------------------------------------------------------- | ||
64 | echo -n "TEST: viewing stats by name..." | ||
65 | |||
66 | if ! $exe $DEBUG -n other > $out; then | ||
67 | echo "FAIL: error running $exe" | ||
68 | $arm -e | ||
69 | exit 1 | ||
70 | fi | ||
71 | LINES=`cat $out | grep 43 | wc -l` | ||
72 | if test $LINES -ne 1; then | ||
73 | echo "FAIL: unexpected output" | ||
74 | $arm -e | ||
75 | exit 1 | ||
76 | fi | ||
77 | echo "PASS" | ||
78 | |||
79 | # ---------------------------------------------------------------------------------- | ||
80 | echo -n "TEST: viewing stats by subsystem..." | ||
81 | |||
82 | if ! $exe $DEBUG -s subsystem > $out; then | ||
83 | echo "FAIL: error running $exe" | ||
84 | $arm -e | ||
85 | exit 1 | ||
86 | fi | ||
87 | LINES=`cat $out | grep 42 | wc -l` | ||
88 | if test $LINES -ne 1; then | ||
89 | echo "FAIL: unexpected output" | ||
90 | $arm -e | ||
91 | exit 1 | ||
92 | fi | ||
93 | echo "PASS" | ||
94 | |||
95 | |||
96 | # ---------------------------------------------------------------------------------- | ||
97 | echo -n "TEST: Set persistent value..." | ||
98 | |||
99 | if ! $exe $DEBUG -n lasting -s subsystem 40 -p; then | ||
100 | echo "FAIL: error running $exe" | ||
101 | $arm -e | ||
102 | exit 1 | ||
103 | fi | ||
104 | if ! $exe $DEBUG > $out; then | ||
105 | echo "FAIL: error running $exe" | ||
106 | $arm -e | ||
107 | exit 1 | ||
108 | fi | ||
109 | LINES=`cat $out | grep 40 | wc -l` | ||
110 | if test $LINES -ne 1; then | ||
111 | echo "FAIL: unexpected output" | ||
112 | cat $out | ||
113 | $arm -e | ||
114 | exit 1 | ||
115 | fi | ||
116 | echo "PASS" | ||
117 | |||
118 | # ----------------------------------- | ||
119 | echo -n "Restarting service..." | ||
120 | $arm -k monkey > /dev/null | ||
121 | sleep 1 | ||
122 | $arm -i monkey > /dev/null | ||
123 | sleep 1 | ||
124 | echo "DONE" | ||
125 | |||
126 | # ---------------------------------------------------------------------------------- | ||
127 | echo -n "TEST: checking persistence..." | ||
128 | |||
129 | if ! $exe $DEBUG > $out; then | ||
130 | echo "FAIL: error running $exe" | ||
131 | $arm -e | ||
132 | exit 1 | ||
133 | fi | ||
134 | LINES=`cat $out | grep 40 | wc -l` | ||
135 | if test $LINES -ne 1; then | ||
136 | echo "FAIL: unexpected output" | ||
137 | cat $out | ||
138 | $arm -e | ||
139 | exit 1 | ||
140 | fi | ||
141 | echo "PASS" | ||
142 | |||
143 | |||
144 | |||
145 | # ---------------------------------------------------------------------------------- | ||
146 | echo -n "TEST: Removing persistence..." | ||
147 | |||
148 | if ! $exe $DEBUG -n lasting -s subsystem 40; then | ||
149 | echo "FAIL: error running $exe" | ||
150 | $arm -e | ||
151 | exit 1 | ||
152 | fi | ||
153 | if ! $exe $DEBUG > $out; then | ||
154 | echo "FAIL: error running $exe" | ||
155 | $arm -e | ||
156 | exit 1 | ||
157 | fi | ||
158 | LINES=`cat $out | grep \! | wc -l` | ||
159 | if test $LINES -ne 0; then | ||
160 | echo "FAIL: unexpected output" | ||
161 | cat $out | ||
162 | $arm -e | ||
163 | exit 1 | ||
164 | fi | ||
165 | echo "PASS" | ||
166 | |||
167 | |||
168 | # ----------------------------------- | ||
169 | echo -n "Restarting service..." | ||
170 | $arm -k monkey > /dev/null | ||
171 | sleep 1 | ||
172 | $arm -i monkey > /dev/null | ||
173 | sleep 1 | ||
174 | echo "DONE" | ||
175 | |||
176 | # ---------------------------------------------------------------------------------- | ||
177 | echo -n "TEST: checking removed persistence..." | ||
178 | |||
179 | if ! $exe $DEBUG > $out; then | ||
180 | echo "FAIL: error running $exe" | ||
181 | $arm -e | ||
182 | exit 1 | ||
183 | fi | ||
184 | LINES=`cat $out | grep 40 | wc -l` | ||
185 | if test $LINES -ne 0; then | ||
186 | echo "FAIL: unexpected output" | ||
187 | cat $out | ||
188 | $arm -e | ||
189 | exit 1 | ||
190 | fi | ||
191 | echo "PASS" | ||
192 | |||
193 | # ----------------------------------- | ||
194 | echo -n "Stopping service..." | ||
195 | $arm -e > /dev/null | ||
196 | sleep 1 | ||
197 | echo "DONE" | ||
198 | rm -f $out | ||
199 | 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 @@ | |||
1 | |||
2 | |||
3 | int main(int argc, char *argv[]) | ||
4 | { | ||
5 | return 0; | ||
6 | } \ 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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Thread commands. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Thread Commands" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Implemented? | ||
12 | -thread-info N.A. | ||
13 | -thread-list-all-threads Yes, implemented as "info threads" | ||
14 | -thread-list-ids Yes | ||
15 | -thread-select Yes | ||
16 | @</pre> | ||
17 | |||
18 | ***************************************************************************/ | ||
19 | |||
20 | #include "mi_gdb.h" | ||
21 | |||
22 | /* Low level versions. */ | ||
23 | |||
24 | void mi_thread_list_ids(mi_h *h) | ||
25 | { | ||
26 | mi_send(h,"-thread-list-ids\n"); | ||
27 | } | ||
28 | |||
29 | void mi_thread_select(mi_h *h, int id) | ||
30 | { | ||
31 | mi_send(h,"-thread-select %d\n",id); | ||
32 | } | ||
33 | |||
34 | void mi_thread_list_all_threads(mi_h *h) | ||
35 | { | ||
36 | mi_send(h,"info threads\n"); | ||
37 | } | ||
38 | |||
39 | /* High level versions. */ | ||
40 | |||
41 | /**[txh]******************************************************************** | ||
42 | |||
43 | Description: | ||
44 | List available thread ids. | ||
45 | |||
46 | Command: -thread-list-ids | ||
47 | Return: !=0 OK | ||
48 | |||
49 | ***************************************************************************/ | ||
50 | |||
51 | int gmi_thread_list_ids(mi_h *h, int **list) | ||
52 | { | ||
53 | mi_thread_list_ids(h); | ||
54 | return mi_res_thread_ids(h,list); | ||
55 | } | ||
56 | |||
57 | /**[txh]******************************************************************** | ||
58 | |||
59 | Description: | ||
60 | Select a thread. | ||
61 | |||
62 | Command: -thread-select | ||
63 | Return: A new mi_frames or NULL on error. | ||
64 | |||
65 | ***************************************************************************/ | ||
66 | |||
67 | mi_frames *gmi_thread_select(mi_h *h, int id) | ||
68 | { | ||
69 | mi_thread_select(h,id); | ||
70 | return mi_res_frame(h); | ||
71 | } | ||
72 | |||
73 | /**[txh]******************************************************************** | ||
74 | |||
75 | Description: | ||
76 | Get a list of frames for each available thread. Implemented using "info | ||
77 | thread". | ||
78 | |||
79 | Command: -thread-list-all-threads | ||
80 | Return: A kist of frames, NULL on error | ||
81 | |||
82 | ***************************************************************************/ | ||
83 | |||
84 | mi_frames *gmi_thread_list_all_threads(mi_h *h) | ||
85 | { | ||
86 | mi_thread_list_all_threads(h); | ||
87 | return mi_res_frames_list(h); | ||
88 | } | ||
89 | |||
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 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: Variable objects. | ||
7 | Comments: | ||
8 | GDB/MI commands for the "Variable Objects" section.@p | ||
9 | |||
10 | @<pre> | ||
11 | gdb command: Imp? Description: | ||
12 | -var-create Yes create a variable object | ||
13 | -var-delete Yes delete the variable object and its children | ||
14 | -var-set-format Yes set the display format of this variable | ||
15 | -var-show-format Yes show the display format of this variable | ||
16 | -var-info-num-children Yes tells how many children this object has | ||
17 | -var-list-children Yes* return a list of the object's children | ||
18 | -var-info-type Yes show the type of this variable object | ||
19 | -var-info-expression Yes print what this variable object represents | ||
20 | -var-show-attributes Yes is this variable editable? | ||
21 | -var-evaluate-expression Yes get the value of this variable | ||
22 | -var-assign Yes set the value of this variable | ||
23 | -var-update Yes* update the variable and its children | ||
24 | @</pre> | ||
25 | |||
26 | Notes:@p | ||
27 | 1) I suggest letting gdb to choose the names for the variables.@* | ||
28 | 2) -var-list-children supports an optional "show values" argument in MI v2. | ||
29 | It isn't implemented.@* | ||
30 | @p | ||
31 | |||
32 | * MI v1 and v2 result formats supported.@p | ||
33 | |||
34 | ***************************************************************************/ | ||
35 | |||
36 | #include "mi_gdb.h" | ||
37 | |||
38 | /* Low level versions. */ | ||
39 | |||
40 | void mi_var_create(mi_h *h, const char *name, int frame, const char *exp) | ||
41 | { | ||
42 | const char *n=name ? name : "-"; | ||
43 | |||
44 | if (frame<0) | ||
45 | mi_send(h,"-var-create %s * %s\n",n,exp); | ||
46 | else | ||
47 | mi_send(h,"-var-create %s %d %s\n",n,frame,exp); | ||
48 | } | ||
49 | |||
50 | void mi_var_delete(mi_h *h, const char *name) | ||
51 | { | ||
52 | mi_send(h,"-var-delete %s\n",name); | ||
53 | } | ||
54 | |||
55 | void mi_var_set_format(mi_h *h, const char *name, const char *format) | ||
56 | { | ||
57 | mi_send(h,"-var-set-format \"%s\" %s\n",name,format); | ||
58 | } | ||
59 | |||
60 | void mi_var_show_format(mi_h *h, const char *name) | ||
61 | { | ||
62 | mi_send(h,"-var-show-format \"%s\"\n",name); | ||
63 | } | ||
64 | |||
65 | void mi_var_info_num_children(mi_h *h, const char *name) | ||
66 | { | ||
67 | mi_send(h,"-var-info-num-children \"%s\"\n",name); | ||
68 | } | ||
69 | |||
70 | void mi_var_info_type(mi_h *h, const char *name) | ||
71 | { | ||
72 | mi_send(h,"-var-info-type \"%s\"\n",name); | ||
73 | } | ||
74 | |||
75 | void mi_var_info_expression(mi_h *h, const char *name) | ||
76 | { | ||
77 | mi_send(h,"-var-info-expression \"%s\"\n",name); | ||
78 | } | ||
79 | |||
80 | void mi_var_show_attributes(mi_h *h, const char *name) | ||
81 | { | ||
82 | mi_send(h,"-var-show-attributes \"%s\"\n",name); | ||
83 | } | ||
84 | |||
85 | void mi_var_update(mi_h *h, const char *name) | ||
86 | { | ||
87 | if (name) | ||
88 | mi_send(h,"-var-update %s\n",name); | ||
89 | else | ||
90 | mi_send(h,"-var-update *\n"); | ||
91 | } | ||
92 | |||
93 | void mi_var_assign(mi_h *h, const char *name, const char *expression) | ||
94 | { | ||
95 | mi_send(h,"-var-assign \"%s\" \"%s\"\n",name,expression); | ||
96 | } | ||
97 | |||
98 | void mi_var_evaluate_expression(mi_h *h, const char *name) | ||
99 | { | ||
100 | mi_send(h,"-var-evaluate-expression \"%s\"\n",name); | ||
101 | } | ||
102 | |||
103 | void mi_var_list_children(mi_h *h, const char *name) | ||
104 | { | ||
105 | if (h->version>=MI_VERSION2U(2,0,0)) | ||
106 | mi_send(h,"-var-list-children --all-values \"%s\"\n",name); | ||
107 | else | ||
108 | mi_send(h,"-var-list-children \"%s\"\n",name); | ||
109 | } | ||
110 | |||
111 | /* High level versions. */ | ||
112 | |||
113 | /**[txh]******************************************************************** | ||
114 | |||
115 | Description: | ||
116 | Create a variable object. I recommend using @x{gmi_var_create} and letting | ||
117 | gdb choose the names. | ||
118 | |||
119 | Command: -var-create | ||
120 | Return: A new mi_gvar strcture or NULL on error. | ||
121 | |||
122 | ***************************************************************************/ | ||
123 | |||
124 | mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp) | ||
125 | { | ||
126 | mi_var_create(h,name,frame,exp); | ||
127 | return mi_res_gvar(h,NULL,exp); | ||
128 | } | ||
129 | |||
130 | /**[txh]******************************************************************** | ||
131 | |||
132 | Description: | ||
133 | Create a variable object. The name is selected by gdb. Alternative: | ||
134 | @x{gmi_full_var_create}. | ||
135 | |||
136 | Command: -var-create [auto name] | ||
137 | Return: A new mi_gvar strcture or NULL on error. | ||
138 | |||
139 | ***************************************************************************/ | ||
140 | |||
141 | mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp) | ||
142 | { | ||
143 | return gmi_var_create_nm(h,NULL,frame,exp); | ||
144 | } | ||
145 | |||
146 | /**[txh]******************************************************************** | ||
147 | |||
148 | Description: | ||
149 | Delete a variable object. Doesn't free the mi_gvar data. | ||
150 | |||
151 | Command: -var-delete | ||
152 | Return: !=0 OK | ||
153 | |||
154 | ***************************************************************************/ | ||
155 | |||
156 | int gmi_var_delete(mi_h *h, mi_gvar *var) | ||
157 | { | ||
158 | mi_var_delete(h,var->name); | ||
159 | return mi_res_simple_done(h); | ||
160 | } | ||
161 | |||
162 | /**[txh]******************************************************************** | ||
163 | |||
164 | Description: | ||
165 | Set the format used to represent the result. | ||
166 | |||
167 | Command: -var-set-format | ||
168 | Return: !=0 OK | ||
169 | |||
170 | ***************************************************************************/ | ||
171 | |||
172 | int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format) | ||
173 | { | ||
174 | int ret; | ||
175 | |||
176 | mi_var_set_format(h,var->name,mi_format_enum_to_str(format)); | ||
177 | ret=mi_res_simple_done(h); | ||
178 | if (ret) | ||
179 | var->format=format; | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | /**[txh]******************************************************************** | ||
184 | |||
185 | Description: | ||
186 | Fill the format field with info from gdb. | ||
187 | |||
188 | Command: -var-show-format | ||
189 | Return: !=0 OK. | ||
190 | |||
191 | ***************************************************************************/ | ||
192 | |||
193 | int gmi_var_show_format(mi_h *h, mi_gvar *var) | ||
194 | { | ||
195 | mi_var_show_format(h,var->name); | ||
196 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
197 | } | ||
198 | |||
199 | /**[txh]******************************************************************** | ||
200 | |||
201 | Description: | ||
202 | Fill the numchild field with info from gdb. | ||
203 | |||
204 | Command: -var-info-num-children | ||
205 | Return: !=0 OK | ||
206 | |||
207 | ***************************************************************************/ | ||
208 | |||
209 | int gmi_var_info_num_children(mi_h *h, mi_gvar *var) | ||
210 | { | ||
211 | mi_var_info_num_children(h,var->name); | ||
212 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
213 | } | ||
214 | |||
215 | /**[txh]******************************************************************** | ||
216 | |||
217 | Description: | ||
218 | Fill the type field with info from gdb. | ||
219 | |||
220 | Command: -var-info-type | ||
221 | Return: !=0 OK | ||
222 | |||
223 | ***************************************************************************/ | ||
224 | |||
225 | int gmi_var_info_type(mi_h *h, mi_gvar *var) | ||
226 | { | ||
227 | mi_var_info_type(h,var->name); | ||
228 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
229 | } | ||
230 | |||
231 | /**[txh]******************************************************************** | ||
232 | |||
233 | Description: | ||
234 | Fill the expression and lang fields with info from gdb. Note that lang | ||
235 | isn't filled during creation. | ||
236 | |||
237 | Command: -var-info-expression | ||
238 | Return: !=0 OK | ||
239 | |||
240 | ***************************************************************************/ | ||
241 | |||
242 | int gmi_var_info_expression(mi_h *h, mi_gvar *var) | ||
243 | { | ||
244 | mi_var_info_expression(h,var->name); | ||
245 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
246 | } | ||
247 | |||
248 | |||
249 | /**[txh]******************************************************************** | ||
250 | |||
251 | Description: | ||
252 | Fill the attr field with info from gdb. Note that attr isn't filled | ||
253 | during creation. | ||
254 | |||
255 | Command: -var-show-attributes | ||
256 | Return: !=0 OK | ||
257 | |||
258 | ***************************************************************************/ | ||
259 | |||
260 | int gmi_var_show_attributes(mi_h *h, mi_gvar *var) | ||
261 | { | ||
262 | mi_var_show_attributes(h,var->name); | ||
263 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
264 | } | ||
265 | |||
266 | /**[txh]******************************************************************** | ||
267 | |||
268 | Description: | ||
269 | Create the variable and also fill the lang and attr fields. The name is | ||
270 | selected by gdb. | ||
271 | |||
272 | Command: -var-create + -var-info-expression + -var-show-attributes | ||
273 | Return: A new mi_gvar strcture or NULL on error. | ||
274 | |||
275 | ***************************************************************************/ | ||
276 | |||
277 | mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp) | ||
278 | { | ||
279 | mi_gvar *var=gmi_var_create_nm(h,NULL,frame,exp); | ||
280 | if (var) | ||
281 | {/* What if it fails? */ | ||
282 | gmi_var_info_expression(h,var); | ||
283 | gmi_var_show_attributes(h,var); | ||
284 | } | ||
285 | return var; | ||
286 | } | ||
287 | |||
288 | /**[txh]******************************************************************** | ||
289 | |||
290 | Description: | ||
291 | Update variable. Use NULL for all. Note that *changed can be NULL if none | ||
292 | updated. | ||
293 | |||
294 | Command: -var-update | ||
295 | Return: !=0 OK. The @var{changed} list contains the list of changed vars. | ||
296 | |||
297 | ***************************************************************************/ | ||
298 | |||
299 | int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed) | ||
300 | { | ||
301 | mi_var_update(h,var ? var->name : NULL); | ||
302 | return mi_res_changelist(h,changed); | ||
303 | } | ||
304 | |||
305 | /**[txh]******************************************************************** | ||
306 | |||
307 | Description: | ||
308 | Change variable. The new value replaces the @var{value} field. | ||
309 | |||
310 | Command: -var-assign | ||
311 | Return: !=0 OK | ||
312 | |||
313 | ***************************************************************************/ | ||
314 | |||
315 | int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression) | ||
316 | { | ||
317 | char *res; | ||
318 | mi_var_assign(h,var->name,expression); | ||
319 | res=mi_res_value(h); | ||
320 | if (res) | ||
321 | { | ||
322 | free(var->value); | ||
323 | var->value=res; | ||
324 | return 1; | ||
325 | } | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | /**[txh]******************************************************************** | ||
330 | |||
331 | Description: | ||
332 | Fill the value field getting the current value for a variable. | ||
333 | |||
334 | Command: -var-evaluate-expression | ||
335 | Return: !=0 OK, value contains the result. | ||
336 | |||
337 | ***************************************************************************/ | ||
338 | |||
339 | int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var) | ||
340 | { | ||
341 | char *s; | ||
342 | |||
343 | mi_var_evaluate_expression(h,var->name); | ||
344 | s=mi_res_value(h); | ||
345 | if (s) | ||
346 | { | ||
347 | free(var->value); | ||
348 | var->value=s; | ||
349 | } | ||
350 | return s!=NULL; | ||
351 | } | ||
352 | |||
353 | /**[txh]******************************************************************** | ||
354 | |||
355 | Description: | ||
356 | List children. It ONLY returns the first level information. :-(@* | ||
357 | On success the child field contains the list of children. | ||
358 | |||
359 | Command: -var-list-children | ||
360 | Return: !=0 OK | ||
361 | |||
362 | ***************************************************************************/ | ||
363 | |||
364 | int gmi_var_list_children(mi_h *h, mi_gvar *var) | ||
365 | { | ||
366 | mi_var_list_children(h,var->name); | ||
367 | return mi_res_children(h,var); | ||
368 | } | ||
369 | |||