diff options
Diffstat (limited to 'pathologist/src/mi')
-rw-r--r-- | pathologist/src/mi/Makefile.am | 29 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_alloc.c | 308 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_breakpoint.c | 265 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_connect.c | 885 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_data_man.c | 243 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_error.c | 38 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_get_free_pty.c | 132 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_get_free_vt.c | 156 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_misc.c | 118 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_parse.c | 1927 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_prg_control.c | 598 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_stack_man.c | 222 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_symbol_query.c | 32 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_target_man.c | 119 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_thread.c | 89 | ||||
-rw-r--r-- | pathologist/src/mi/gdbmi_var_obj.c | 371 |
16 files changed, 5532 insertions, 0 deletions
diff --git a/pathologist/src/mi/Makefile.am b/pathologist/src/mi/Makefile.am new file mode 100644 index 0000000..1248080 --- /dev/null +++ b/pathologist/src/mi/Makefile.am | |||
@@ -0,0 +1,29 @@ | |||
1 | INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/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 | lib_LTLIBRARIES = libmi.la | ||
13 | |||
14 | libmi_la_SOURCES = \ | ||
15 | gdbmi_alloc.c \ | ||
16 | gdbmi_breakpoint.c \ | ||
17 | gdbmi_connect.c \ | ||
18 | gdbmi_data_man.c \ | ||
19 | gdbmi_error.c \ | ||
20 | gdbmi_get_free_pty.c \ | ||
21 | gdbmi_get_free_vt.c \ | ||
22 | gdbmi_misc.c \ | ||
23 | gdbmi_parse.c \ | ||
24 | gdbmi_prg_control.c \ | ||
25 | gdbmi_stack_man.c \ | ||
26 | gdbmi_symbol_query.c \ | ||
27 | gdbmi_target_man.c \ | ||
28 | gdbmi_thread.c \ | ||
29 | gdbmi_var_obj.c \ No newline at end of file | ||
diff --git a/pathologist/src/mi/gdbmi_alloc.c b/pathologist/src/mi/gdbmi_alloc.c new file mode 100644 index 0000000..0fe6e14 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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/pathologist/src/mi/gdbmi_breakpoint.c b/pathologist/src/mi/gdbmi_breakpoint.c new file mode 100644 index 0000000..f91cc3e --- /dev/null +++ b/pathologist/src/mi/gdbmi_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. | ||
9 | @<p> | ||
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 "gdbmi.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/pathologist/src/mi/gdbmi_connect.c b/pathologist/src/mi/gdbmi_connect.c new file mode 100644 index 0000000..b9f3754 --- /dev/null +++ b/pathologist/src/mi/gdbmi_connect.c | |||
@@ -0,0 +1,885 @@ | |||
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. | ||
10 | @<p> | ||
11 | |||
12 | GDB Bug workaround for "file -readnow": I tried to workaround a bug using | ||
13 | it but looks like this option also have bugs!!!! so I have to use the | ||
14 | command line option --readnow. | ||
15 | It also have a bug!!!! when the binary is changed and gdb must reload it | ||
16 | this option is ignored. So it looks like we have no solution but 3 gdb bugs | ||
17 | in a row. | ||
18 | |||
19 | ***************************************************************************/ | ||
20 | |||
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 "plibc.h" | ||
35 | |||
36 | #include "gdbmi.h" | ||
37 | #ifndef _GNU_SOURCE | ||
38 | #define _GNU_SOURCE | ||
39 | #endif | ||
40 | |||
41 | #ifndef TEMP_FAILURE_RETRY | ||
42 | #define TEMP_FAILURE_RETRY(a) (a) | ||
43 | #endif | ||
44 | |||
45 | int mi_error=MI_OK; | ||
46 | char *mi_error_from_gdb=NULL; | ||
47 | static char *gdb_exe=NULL; | ||
48 | static char *xterm_exe=NULL; | ||
49 | static char *gdb_start=NULL; | ||
50 | static char *gdb_conn=NULL; | ||
51 | static char *main_func=NULL; | ||
52 | static char disable_psym_search_workaround=0; | ||
53 | |||
54 | mi_h *mi_alloc_h() | ||
55 | { | ||
56 | mi_h *h=(mi_h *)calloc(1,sizeof(mi_h)); | ||
57 | if (!h) | ||
58 | { | ||
59 | mi_error=MI_OUT_OF_MEMORY; | ||
60 | return NULL; | ||
61 | } | ||
62 | h->to_gdb[0]=h->to_gdb[1]=h->from_gdb[0]=h->from_gdb[1]=-1; | ||
63 | h->pid=-1; | ||
64 | return h; | ||
65 | } | ||
66 | |||
67 | int mi_check_running_pid(pid_t pid) | ||
68 | { | ||
69 | int status; | ||
70 | |||
71 | if (pid<=0) | ||
72 | return 0; | ||
73 | /* If waitpid returns the number of our child means it communicated | ||
74 | to as a termination status. */ | ||
75 | if (waitpid(pid,&status,WNOHANG)==pid) | ||
76 | { | ||
77 | pid=0; | ||
78 | return 0; | ||
79 | } | ||
80 | return 1; | ||
81 | } | ||
82 | |||
83 | int mi_check_running(mi_h *h) | ||
84 | { | ||
85 | return !h->died && mi_check_running_pid(h->pid); | ||
86 | } | ||
87 | |||
88 | void mi_kill_child(pid_t pid) | ||
89 | { | ||
90 | kill(pid,SIGTERM); | ||
91 | usleep(100000); | ||
92 | if (mi_check_running_pid(pid)) | ||
93 | { | ||
94 | int status; | ||
95 | kill(pid,SIGKILL); | ||
96 | waitpid(pid,&status,0); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | void mi_free_h(mi_h **handle) | ||
101 | { | ||
102 | mi_h *h=*handle; | ||
103 | if (h->to_gdb[0]>=0) | ||
104 | close(h->to_gdb[0]); | ||
105 | if (h->to) | ||
106 | fclose(h->to); | ||
107 | else if (h->to_gdb[1]>=0) | ||
108 | close(h->to_gdb[1]); | ||
109 | if (h->from) | ||
110 | fclose(h->from); | ||
111 | else if (h->from_gdb[0]>=0) | ||
112 | close(h->from_gdb[0]); | ||
113 | if (h->from_gdb[1]>=0) | ||
114 | close(h->from_gdb[1]); | ||
115 | if (mi_check_running(h)) | ||
116 | {/* GDB is running! */ | ||
117 | mi_kill_child(h->pid); | ||
118 | } | ||
119 | if (h->line) | ||
120 | free(h->line); | ||
121 | mi_free_output(h->po); | ||
122 | free(h->catched_console); | ||
123 | free(h); | ||
124 | *handle=NULL; | ||
125 | } | ||
126 | |||
127 | void mi_set_nonblk(int h) | ||
128 | { | ||
129 | int flf; | ||
130 | flf=fcntl(h,F_GETFL,0); | ||
131 | flf=flf | O_NONBLOCK; | ||
132 | fcntl(h,F_SETFL,flf); | ||
133 | } | ||
134 | |||
135 | int mi_getline(mi_h *h) | ||
136 | { | ||
137 | char c; | ||
138 | |||
139 | while (read(h->from_gdb[0],&c,1)==1) | ||
140 | { | ||
141 | if (h->lread>=h->llen) | ||
142 | { | ||
143 | h->llen=h->lread+128; | ||
144 | h->line=(char *)realloc(h->line,h->llen); | ||
145 | if (!h->line) | ||
146 | { | ||
147 | h->llen=0; | ||
148 | h->lread=0; | ||
149 | return -1; | ||
150 | } | ||
151 | } | ||
152 | if (c=='\n') | ||
153 | { | ||
154 | int ret=h->lread; | ||
155 | h->line[ret]=0; | ||
156 | h->lread=0; | ||
157 | return ret; | ||
158 | } | ||
159 | h->line[h->lread]=c; | ||
160 | h->lread++; | ||
161 | } | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | char *get_cstr(mi_output *o) | ||
166 | { | ||
167 | if (!o->c || o->c->type!=t_const) | ||
168 | return NULL; | ||
169 | return o->c->v.cstr; | ||
170 | } | ||
171 | |||
172 | int mi_get_response(mi_h *h) | ||
173 | { | ||
174 | int l=mi_getline(h); | ||
175 | if (!l) | ||
176 | return 0; | ||
177 | |||
178 | if (h->from_gdb_echo) | ||
179 | h->from_gdb_echo(h->line,h->from_gdb_echo_data); | ||
180 | if (strncmp(h->line,"(gdb)",5)==0) | ||
181 | {/* End of response. */ | ||
182 | return 1; | ||
183 | } | ||
184 | else | ||
185 | {/* Add to the response. */ | ||
186 | mi_output *o; | ||
187 | int add=1, is_exit=0; | ||
188 | o=mi_parse_gdb_output(h->line); | ||
189 | |||
190 | if (!o) | ||
191 | return 0; | ||
192 | /* Tunneled streams callbacks. */ | ||
193 | if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_STREAM) | ||
194 | { | ||
195 | char *aux; | ||
196 | add=0; | ||
197 | switch (o->sstype) | ||
198 | { | ||
199 | case MI_SST_CONSOLE: | ||
200 | aux=get_cstr(o); | ||
201 | if (h->console) | ||
202 | h->console(aux,h->console_data); | ||
203 | if (h->catch_console && aux) | ||
204 | { | ||
205 | h->catch_console--; | ||
206 | if (!h->catch_console) | ||
207 | { | ||
208 | free(h->catched_console); | ||
209 | h->catched_console=strdup(aux); | ||
210 | } | ||
211 | } | ||
212 | break; | ||
213 | case MI_SST_TARGET: | ||
214 | /* This one seems to be useless. */ | ||
215 | if (h->target) | ||
216 | h->target(get_cstr(o),h->target_data); | ||
217 | break; | ||
218 | case MI_SST_LOG: | ||
219 | if (h->log) | ||
220 | h->log(get_cstr(o),h->log_data); | ||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | else if (o->type==MI_T_OUT_OF_BAND && o->stype==MI_ST_ASYNC) | ||
225 | { | ||
226 | if (h->async) | ||
227 | h->async(o,h->async_data); | ||
228 | } | ||
229 | else if (o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_ERROR) | ||
230 | {/* Error from gdb, record it. */ | ||
231 | mi_error=MI_FROM_GDB; | ||
232 | free(mi_error_from_gdb); | ||
233 | mi_error_from_gdb=NULL; | ||
234 | if (o->c && strcmp(o->c->var,"msg")==0 && o->c->type==t_const) | ||
235 | mi_error_from_gdb=strdup(o->c->v.cstr); | ||
236 | } | ||
237 | is_exit=(o->type==MI_T_RESULT_RECORD && o->tclass==MI_CL_EXIT); | ||
238 | /* Add to the list of responses. */ | ||
239 | if (add) | ||
240 | { | ||
241 | if (h->last) | ||
242 | h->last->next=o; | ||
243 | else | ||
244 | h->po=o; | ||
245 | h->last=o; | ||
246 | } | ||
247 | else | ||
248 | mi_free_output(o); | ||
249 | /* Exit RR means gdb exited, we won't get a new prompt ;-) */ | ||
250 | if (is_exit) | ||
251 | return 1; | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | mi_output *mi_retire_response(mi_h *h) | ||
258 | { | ||
259 | mi_output *ret=h->po; | ||
260 | h->po=h->last=NULL; | ||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | mi_output *mi_get_response_blk(mi_h *h) | ||
265 | { | ||
266 | int r; | ||
267 | /* Sometimes gdb dies. */ | ||
268 | if (!mi_check_running(h)) | ||
269 | { | ||
270 | h->died=1; | ||
271 | mi_error=MI_GDB_DIED; | ||
272 | return NULL; | ||
273 | } | ||
274 | do | ||
275 | { | ||
276 | /* | ||
277 | That's a must. If we just keep trying to read and failing things | ||
278 | become really sloooowwww. Instead we try and if it fails we wait | ||
279 | until something is available. | ||
280 | TODO: Implement something with the time out, a callback to ask the | ||
281 | application is we have to wait or not could be a good thing. | ||
282 | */ | ||
283 | fd_set set; | ||
284 | struct timeval timeout; | ||
285 | int ret; | ||
286 | |||
287 | r=mi_get_response(h); | ||
288 | if (r) | ||
289 | return mi_retire_response(h); | ||
290 | |||
291 | FD_ZERO(&set); | ||
292 | FD_SET(h->from_gdb[0],&set); | ||
293 | timeout.tv_sec=h->time_out; | ||
294 | timeout.tv_usec=0; | ||
295 | ret=TEMP_FAILURE_RETRY(select(FD_SETSIZE,&set,NULL,NULL,&timeout)); | ||
296 | if (!ret) | ||
297 | { | ||
298 | if (!mi_check_running(h)) | ||
299 | { | ||
300 | h->died=1; | ||
301 | mi_error=MI_GDB_DIED; | ||
302 | return NULL; | ||
303 | } | ||
304 | if (h->time_out_cb) | ||
305 | ret=h->time_out_cb(h->time_out_cb_data); | ||
306 | if (!ret) | ||
307 | { | ||
308 | mi_error=MI_GDB_TIME_OUT; | ||
309 | return NULL; | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | while (!r); | ||
314 | |||
315 | return NULL; | ||
316 | } | ||
317 | |||
318 | void mi_send_commands(mi_h *h, const char *file) | ||
319 | { | ||
320 | FILE *f; | ||
321 | char b[PATH_MAX]; | ||
322 | |||
323 | //printf("File: %s\n",file); | ||
324 | if (!file) | ||
325 | return; | ||
326 | f=fopen(file,"rt"); | ||
327 | if (!f) | ||
328 | return; | ||
329 | while (!feof(f)) | ||
330 | { | ||
331 | if (fgets(b,PATH_MAX,f)) | ||
332 | { | ||
333 | //printf("Send: %s\n",b); | ||
334 | mi_send (h, "%s", b); | ||
335 | mi_res_simple_done(h); | ||
336 | } | ||
337 | } | ||
338 | fclose(f); | ||
339 | } | ||
340 | |||
341 | void mi_send_target_commands(mi_h *h) | ||
342 | { | ||
343 | mi_send_commands(h,gdb_conn); | ||
344 | } | ||
345 | |||
346 | /**[txh]******************************************************************** | ||
347 | |||
348 | Description: | ||
349 | Connect to a local copy of gdb. Note that the mi_h structure is something | ||
350 | similar to a "FILE *" for stdio. | ||
351 | |||
352 | Return: A new mi_h structure or NULL on error. | ||
353 | |||
354 | ***************************************************************************/ | ||
355 | |||
356 | mi_h *mi_connect_local() | ||
357 | { | ||
358 | mi_h *h; | ||
359 | const char *gdb=mi_get_gdb_exe(); | ||
360 | |||
361 | /* Start without error. */ | ||
362 | mi_error=MI_OK; | ||
363 | /* Verify we have a GDB binary. */ | ||
364 | if (access(gdb,X_OK)) | ||
365 | { | ||
366 | mi_error=MI_MISSING_GDB; | ||
367 | return NULL; | ||
368 | } | ||
369 | /* Alloc the handle structure. */ | ||
370 | h=mi_alloc_h(); | ||
371 | if (!h) | ||
372 | return h; | ||
373 | h->time_out=MI_DEFAULT_TIME_OUT; | ||
374 | /* Create the pipes to connect with the child. */ | ||
375 | if (pipe(h->to_gdb) || pipe(h->from_gdb)) | ||
376 | { | ||
377 | mi_error=MI_PIPE_CREATE; | ||
378 | mi_free_h(&h); | ||
379 | return NULL; | ||
380 | } | ||
381 | mi_set_nonblk(h->to_gdb[1]); | ||
382 | mi_set_nonblk(h->from_gdb[0]); | ||
383 | /* Associate streams to the file handles. */ | ||
384 | h->to=fdopen(h->to_gdb[1],"w"); | ||
385 | h->from=fdopen(h->from_gdb[0],"r"); | ||
386 | if (!h->to || !h->from) | ||
387 | { | ||
388 | mi_error=MI_PIPE_CREATE; | ||
389 | mi_free_h(&h); | ||
390 | return NULL; | ||
391 | } | ||
392 | /* Create the child. */ | ||
393 | h->pid=fork(); | ||
394 | if (h->pid==0) | ||
395 | {/* We are the child. */ | ||
396 | char *argv[5]; | ||
397 | /* Connect stdin/out to the pipes. */ | ||
398 | dup2(h->to_gdb[0],STDIN_FILENO); | ||
399 | dup2(h->from_gdb[1],STDOUT_FILENO); | ||
400 | /* Pass the control to gdb. */ | ||
401 | argv[0]=(char *)gdb; /* Is that OK? */ | ||
402 | argv[1]="--interpreter=mi"; | ||
403 | argv[2]="--quiet"; | ||
404 | argv[3]=disable_psym_search_workaround ? 0 : "--readnow"; | ||
405 | argv[4]=0; | ||
406 | execvp(argv[0],argv); | ||
407 | /* We get here only if exec failed. */ | ||
408 | _exit(127); | ||
409 | } | ||
410 | /* We are the parent. */ | ||
411 | if (h->pid==-1) | ||
412 | {/* Fork failed. */ | ||
413 | mi_error=MI_FORK; | ||
414 | mi_free_h(&h); | ||
415 | return NULL; | ||
416 | } | ||
417 | if (!mi_check_running(h)) | ||
418 | { | ||
419 | mi_error=MI_DEBUGGER_RUN; | ||
420 | mi_free_h(&h); | ||
421 | return NULL; | ||
422 | } | ||
423 | /* Wait for the prompt. */ | ||
424 | mi_get_response_blk(h); | ||
425 | /* Send the start-up commands */ | ||
426 | mi_send_commands(h,gdb_start); | ||
427 | |||
428 | return h; | ||
429 | } | ||
430 | |||
431 | /**[txh]******************************************************************** | ||
432 | |||
433 | Description: | ||
434 | Close connection. You should ask gdb to quit first gmi_gdb_exit. | ||
435 | |||
436 | ***************************************************************************/ | ||
437 | |||
438 | void mi_disconnect(mi_h *h) | ||
439 | { | ||
440 | mi_free_h(&h); | ||
441 | free(mi_error_from_gdb); | ||
442 | mi_error_from_gdb=NULL; | ||
443 | } | ||
444 | |||
445 | void mi_set_console_cb(mi_h *h, stream_cb cb, void *data) | ||
446 | { | ||
447 | h->console=cb; | ||
448 | h->console_data=data; | ||
449 | } | ||
450 | |||
451 | void mi_set_target_cb(mi_h *h, stream_cb cb, void *data) | ||
452 | { | ||
453 | h->target=cb; | ||
454 | h->target_data=data; | ||
455 | } | ||
456 | |||
457 | void mi_set_log_cb(mi_h *h, stream_cb cb, void *data) | ||
458 | { | ||
459 | h->log=cb; | ||
460 | h->log_data=data; | ||
461 | } | ||
462 | |||
463 | stream_cb mi_get_console_cb(mi_h *h, void **data) | ||
464 | { | ||
465 | if (data) | ||
466 | *data=h->console_data; | ||
467 | return h->console; | ||
468 | } | ||
469 | |||
470 | stream_cb mi_get_target_cb(mi_h *h, void **data) | ||
471 | { | ||
472 | if (data) | ||
473 | *data=h->target_data; | ||
474 | return h->target; | ||
475 | } | ||
476 | |||
477 | stream_cb mi_get_log_cb(mi_h *h, void **data) | ||
478 | { | ||
479 | if (data) | ||
480 | *data=h->log_data; | ||
481 | return h->log; | ||
482 | } | ||
483 | |||
484 | void mi_set_async_cb(mi_h *h, async_cb cb, void *data) | ||
485 | { | ||
486 | h->async=cb; | ||
487 | h->async_data=data; | ||
488 | } | ||
489 | |||
490 | async_cb mi_get_async_cb(mi_h *h, void **data) | ||
491 | { | ||
492 | if (data) | ||
493 | *data=h->async_data; | ||
494 | return h->async; | ||
495 | } | ||
496 | |||
497 | void mi_set_to_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
498 | { | ||
499 | h->to_gdb_echo=cb; | ||
500 | h->to_gdb_echo_data=data; | ||
501 | } | ||
502 | |||
503 | void mi_set_from_gdb_cb(mi_h *h, stream_cb cb, void *data) | ||
504 | { | ||
505 | h->from_gdb_echo=cb; | ||
506 | h->from_gdb_echo_data=data; | ||
507 | } | ||
508 | |||
509 | stream_cb mi_get_to_gdb_cb(mi_h *h, void **data) | ||
510 | { | ||
511 | if (data) | ||
512 | *data=h->to_gdb_echo_data; | ||
513 | return h->to_gdb_echo; | ||
514 | } | ||
515 | |||
516 | stream_cb mi_get_from_gdb_cb(mi_h *h, void **data) | ||
517 | { | ||
518 | if (data) | ||
519 | *data=h->from_gdb_echo_data; | ||
520 | return h->from_gdb_echo; | ||
521 | } | ||
522 | |||
523 | void mi_set_time_out_cb(mi_h *h, tm_cb cb, void *data) | ||
524 | { | ||
525 | h->time_out_cb=cb; | ||
526 | h->time_out_cb_data=data; | ||
527 | } | ||
528 | |||
529 | tm_cb mi_get_time_out_cb(mi_h *h, void **data) | ||
530 | { | ||
531 | if (data) | ||
532 | *data=h->time_out_cb_data; | ||
533 | return h->time_out_cb; | ||
534 | } | ||
535 | |||
536 | void mi_set_time_out(mi_h *h, int to) | ||
537 | { | ||
538 | h->time_out=to; | ||
539 | } | ||
540 | |||
541 | int mi_get_time_out(mi_h *h) | ||
542 | { | ||
543 | return h->time_out; | ||
544 | } | ||
545 | |||
546 | int mi_send(mi_h *h, const char *format, ...) | ||
547 | { | ||
548 | int ret; | ||
549 | char *str; | ||
550 | va_list argptr; | ||
551 | |||
552 | if (h->died) | ||
553 | return 0; | ||
554 | |||
555 | va_start(argptr,format); | ||
556 | ret=vasprintf(&str,format,argptr); | ||
557 | va_end(argptr); | ||
558 | if (-1 != ret) | ||
559 | { | ||
560 | fputs(str,h->to); | ||
561 | fflush(h->to); | ||
562 | if (h->to_gdb_echo) | ||
563 | h->to_gdb_echo(str,h->to_gdb_echo_data); | ||
564 | free(str); | ||
565 | } | ||
566 | else | ||
567 | { | ||
568 | abort (); | ||
569 | } | ||
570 | |||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | void mi_clean_up_globals() | ||
575 | { | ||
576 | free(gdb_exe); | ||
577 | gdb_exe=NULL; | ||
578 | free(xterm_exe); | ||
579 | xterm_exe=NULL; | ||
580 | free(gdb_start); | ||
581 | gdb_start=NULL; | ||
582 | free(gdb_conn); | ||
583 | gdb_conn=NULL; | ||
584 | free(main_func); | ||
585 | main_func=NULL; | ||
586 | } | ||
587 | |||
588 | void mi_register_exit() | ||
589 | { | ||
590 | static int registered=0; | ||
591 | if (!registered) | ||
592 | { | ||
593 | registered=1; | ||
594 | atexit(mi_clean_up_globals); | ||
595 | } | ||
596 | } | ||
597 | |||
598 | void mi_set_gdb_exe(const char *name) | ||
599 | { | ||
600 | free(gdb_exe); | ||
601 | gdb_exe=name ? strdup(name) : NULL; | ||
602 | mi_register_exit(); | ||
603 | } | ||
604 | |||
605 | void mi_set_gdb_start(const char *name) | ||
606 | { | ||
607 | free(gdb_start); | ||
608 | gdb_start=name ? strdup(name) : NULL; | ||
609 | mi_register_exit(); | ||
610 | } | ||
611 | |||
612 | void mi_set_gdb_conn(const char *name) | ||
613 | { | ||
614 | free(gdb_conn); | ||
615 | gdb_conn=name ? strdup(name) : NULL; | ||
616 | mi_register_exit(); | ||
617 | } | ||
618 | |||
619 | static | ||
620 | char *mi_search_in_path(const char *file) | ||
621 | { | ||
622 | char *path, *pt, *r; | ||
623 | char test[PATH_MAX]; | ||
624 | struct stat st; | ||
625 | |||
626 | path=getenv("PATH"); | ||
627 | if (!path) | ||
628 | return NULL; | ||
629 | pt=strdup(path); | ||
630 | r=strtok(pt,PATH_SEPARATOR_STR); | ||
631 | while (r) | ||
632 | { | ||
633 | strcpy(test,r); | ||
634 | strcat(test,"/"); | ||
635 | strcat(test,file); | ||
636 | if (stat(test,&st)==0 && S_ISREG(st.st_mode)) | ||
637 | { | ||
638 | free(pt); | ||
639 | return strdup(test); | ||
640 | } | ||
641 | r=strtok(NULL,PATH_SEPARATOR_STR); | ||
642 | } | ||
643 | free(pt); | ||
644 | return NULL; | ||
645 | } | ||
646 | |||
647 | const char *mi_get_gdb_exe() | ||
648 | { | ||
649 | if (!gdb_exe) | ||
650 | {/* Look for gdb in path */ | ||
651 | gdb_exe=mi_search_in_path("gdb"); | ||
652 | if (!gdb_exe) | ||
653 | return "/usr/bin/gdb"; | ||
654 | } | ||
655 | return gdb_exe; | ||
656 | } | ||
657 | |||
658 | const char *mi_get_gdb_start() | ||
659 | { | ||
660 | return gdb_start; | ||
661 | } | ||
662 | |||
663 | const char *mi_get_gdb_conn() | ||
664 | { | ||
665 | return gdb_conn; | ||
666 | } | ||
667 | |||
668 | void mi_set_xterm_exe(const char *name) | ||
669 | { | ||
670 | free(xterm_exe); | ||
671 | xterm_exe=name ? strdup(name) : NULL; | ||
672 | mi_register_exit(); | ||
673 | } | ||
674 | |||
675 | const char *mi_get_xterm_exe() | ||
676 | { | ||
677 | if (!xterm_exe) | ||
678 | {/* Look for xterm in path */ | ||
679 | xterm_exe=mi_search_in_path("xterm"); | ||
680 | if (!xterm_exe) | ||
681 | return "/usr/bin/X11/xterm"; | ||
682 | } | ||
683 | return xterm_exe; | ||
684 | } | ||
685 | |||
686 | void mi_set_main_func(const char *name) | ||
687 | { | ||
688 | free(main_func); | ||
689 | main_func=name ? strdup(name) : NULL; | ||
690 | mi_register_exit(); | ||
691 | } | ||
692 | |||
693 | const char *mi_get_main_func() | ||
694 | { | ||
695 | if (main_func) | ||
696 | return main_func; | ||
697 | return "main"; | ||
698 | } | ||
699 | |||
700 | /**[txh]******************************************************************** | ||
701 | |||
702 | Description: | ||
703 | Opens a new xterm to be used by the child process to debug. | ||
704 | |||
705 | Return: A new mi_aux_term structure, you can use gmi_end_aux_term to | ||
706 | release it. | ||
707 | |||
708 | ***************************************************************************/ | ||
709 | |||
710 | mi_aux_term *gmi_start_xterm() | ||
711 | { | ||
712 | char nsh[14]="/tmp/shXXXXXX"; | ||
713 | char ntt[14]="/tmp/ttXXXXXX"; | ||
714 | const char *xterm; | ||
715 | struct stat st; | ||
716 | int hsh, htt=-1; | ||
717 | mi_aux_term *res=NULL; | ||
718 | FILE *f; | ||
719 | pid_t pid; | ||
720 | char buf[PATH_MAX]; | ||
721 | |||
722 | /* Verify we have an X terminal. */ | ||
723 | xterm=mi_get_xterm_exe(); | ||
724 | if (access(xterm,X_OK)) | ||
725 | { | ||
726 | mi_error=MI_MISSING_XTERM; | ||
727 | return NULL; | ||
728 | } | ||
729 | |||
730 | /* Create 2 temporals. */ | ||
731 | hsh=mkstemp(nsh); | ||
732 | if (hsh==-1) | ||
733 | { | ||
734 | mi_error=MI_CREATE_TEMPORAL; | ||
735 | return NULL; | ||
736 | } | ||
737 | htt=mkstemp(ntt); | ||
738 | if (htt==-1) | ||
739 | { | ||
740 | close(hsh); | ||
741 | unlink(nsh); | ||
742 | mi_error=MI_CREATE_TEMPORAL; | ||
743 | return NULL; | ||
744 | } | ||
745 | close(htt); | ||
746 | /* Create the script. */ | ||
747 | f=fdopen(hsh,"w"); | ||
748 | if (!f) | ||
749 | { | ||
750 | close(hsh); | ||
751 | unlink(nsh); | ||
752 | unlink(ntt); | ||
753 | mi_error=MI_CREATE_TEMPORAL; | ||
754 | return NULL; | ||
755 | } | ||
756 | fprintf(f,"#!/bin/sh\n"); | ||
757 | fprintf(f,"tty > %s\n",ntt); | ||
758 | fprintf(f,"rm %s\n",nsh); | ||
759 | fprintf(f,"sleep 365d\n"); | ||
760 | fclose(f); | ||
761 | /* Spawn xterm. */ | ||
762 | /* Create the child. */ | ||
763 | pid=fork(); | ||
764 | if (pid==0) | ||
765 | {/* We are the child. */ | ||
766 | char *argv[5]; | ||
767 | /* Pass the control to gdb. */ | ||
768 | argv[0]=(char *)mi_get_xterm_exe(); /* Is that ok? */ | ||
769 | argv[1]="-e"; | ||
770 | argv[2]="/bin/sh"; | ||
771 | argv[3]=nsh; | ||
772 | argv[4]=0; | ||
773 | execvp(argv[0],argv); | ||
774 | /* We get here only if exec failed. */ | ||
775 | unlink(nsh); | ||
776 | unlink(ntt); | ||
777 | _exit(127); | ||
778 | } | ||
779 | /* We are the parent. */ | ||
780 | if (pid==-1) | ||
781 | {/* Fork failed. */ | ||
782 | unlink(nsh); | ||
783 | unlink(ntt); | ||
784 | mi_error=MI_FORK; | ||
785 | return NULL; | ||
786 | } | ||
787 | /* Wait until the shell is deleted. */ | ||
788 | while (stat(nsh,&st)==0) | ||
789 | usleep(1000); | ||
790 | /* Try to read the tty name. */ | ||
791 | f=fopen(ntt,"rt"); | ||
792 | if (f) | ||
793 | { | ||
794 | if (fgets(buf,PATH_MAX,f)) | ||
795 | { | ||
796 | char *s; /* Strip the \n. */ | ||
797 | for (s=buf; *s && *s!='\n'; s++); | ||
798 | *s=0; | ||
799 | res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); | ||
800 | if (res) | ||
801 | { | ||
802 | res->pid=pid; | ||
803 | res->tty=strdup(buf); | ||
804 | } | ||
805 | } | ||
806 | fclose(f); | ||
807 | } | ||
808 | unlink(ntt); | ||
809 | return res; | ||
810 | } | ||
811 | |||
812 | void mi_free_aux_term(mi_aux_term *t) | ||
813 | { | ||
814 | if (!t) | ||
815 | return; | ||
816 | free(t->tty); | ||
817 | free(t); | ||
818 | } | ||
819 | |||
820 | /**[txh]******************************************************************** | ||
821 | |||
822 | Description: | ||
823 | Closes the auxiliar terminal and releases the allocated memory. | ||
824 | |||
825 | ***************************************************************************/ | ||
826 | |||
827 | void gmi_end_aux_term(mi_aux_term *t) | ||
828 | { | ||
829 | if (!t) | ||
830 | return; | ||
831 | if (t->pid!=-1 && mi_check_running_pid(t->pid)) | ||
832 | mi_kill_child(t->pid); | ||
833 | mi_free_aux_term(t); | ||
834 | } | ||
835 | |||
836 | /**[txh]******************************************************************** | ||
837 | |||
838 | Description: | ||
839 | Forces the MI version. Currently the library can't detect it so you must | ||
840 | force it manually. GDB 5.x implemented MI v1 and 6.x v2. | ||
841 | |||
842 | ***************************************************************************/ | ||
843 | |||
844 | void mi_force_version(mi_h *h, unsigned vMajor, unsigned vMiddle, | ||
845 | unsigned vMinor) | ||
846 | { | ||
847 | h->version=MI_VERSION2U(vMajor,vMiddle,vMinor); | ||
848 | } | ||
849 | |||
850 | /**[txh]******************************************************************** | ||
851 | |||
852 | Description: | ||
853 | Dis/Enables the workaround for a bug in gdb. | ||
854 | |||
855 | ***************************************************************************/ | ||
856 | |||
857 | void mi_set_workaround(unsigned wa, int enable) | ||
858 | { | ||
859 | switch (wa) | ||
860 | { | ||
861 | case MI_PSYM_SEARCH: | ||
862 | disable_psym_search_workaround=enable ? 0 : 1; | ||
863 | break; | ||
864 | } | ||
865 | } | ||
866 | |||
867 | /**[txh]******************************************************************** | ||
868 | |||
869 | Description: | ||
870 | Finds if the workaround for a bug in gdb is enabled. | ||
871 | |||
872 | Return: !=0 if enabled. | ||
873 | |||
874 | ***************************************************************************/ | ||
875 | |||
876 | int mi_get_workaround(unsigned wa) | ||
877 | { | ||
878 | switch (wa) | ||
879 | { | ||
880 | case MI_PSYM_SEARCH: | ||
881 | return disable_psym_search_workaround==0; | ||
882 | } | ||
883 | return 0; | ||
884 | } | ||
885 | |||
diff --git a/pathologist/src/mi/gdbmi_data_man.c b/pathologist/src/mi/gdbmi_data_man.c new file mode 100644 index 0000000..06e1377 --- /dev/null +++ b/pathologist/src/mi/gdbmi_data_man.c | |||
@@ -0,0 +1,243 @@ | |||
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. | ||
9 | |||
10 | @<p> | ||
11 | |||
12 | @<pre> | ||
13 | gdb command: Implemented? | ||
14 | |||
15 | -data-disassemble Yes | ||
16 | -data-evaluate-expression Yes | ||
17 | -data-list-changed-registers No | ||
18 | -data-list-register-names Yes | ||
19 | -data-list-register-values No | ||
20 | -data-read-memory No | ||
21 | -display-delete N.A. (delete display) | ||
22 | -display-disable N.A. (disable display) | ||
23 | -display-enable N.A. (enable display) | ||
24 | -display-insert N.A. (display) | ||
25 | -display-list N.A. (info display) | ||
26 | -environment-cd No | ||
27 | -environment-directory Yes, MI v1 implementation | ||
28 | -environment-path No | ||
29 | @</pre> | ||
30 | |||
31 | Notes: @<p> | ||
32 | |||
33 | 1) -display* aren't implemented. You can use CLI command display, but the | ||
34 | results are sent to the console. So it looks like the best is to manually | ||
35 | use -data-evaluate-expression to emulate it. @<p> | ||
36 | |||
37 | 2) GDB bug mi/1770: Affects gdb<=6.2, when you ask for the names of the | ||
38 | registers you get it plus the name of the "pseudo-registers", but if you | ||
39 | try to get the value of a pseudo-register you get an error saying the | ||
40 | register number is invalid. I reported to gdb-patches@sources.redhat.com | ||
41 | on 2004/08/25 and as I didn't get any answer I filled a bug report on | ||
42 | 2004/09/02. The patch to fix this annoying bug is: | ||
43 | |||
44 | Index: gdb/mi/mi-main.c | ||
45 | =================================================================== | ||
46 | RCS file: /cvs/src/src/gdb/mi/mi-main.c,v | ||
47 | retrieving revision 1.64 | ||
48 | diff -u -r1.64 mi-main.c | ||
49 | --- gdb/mi/mi-main.c 3 Aug 2004 00:57:27 -0000 1.64 | ||
50 | +++ gdb/mi/mi-main.c 25 Aug 2004 14:12:50 -0000 | ||
51 | @@ -423,7 +423,7 @@ | ||
52 | case, some entries of REGISTER_NAME will change depending upon | ||
53 | the particular processor being debugged. | ||
54 | |||
55 | - numregs = NUM_REGS; | ||
56 | + numregs = NUM_REGS + NUM_PSEUDO_REGS; | ||
57 | |||
58 | if (argc == 0) | ||
59 | { | ||
60 | ---- | ||
61 | |||
62 | Note I had to remove an end of comment in the patch to include it here. | ||
63 | This bug forced me to create another set of functions. The only way is to | ||
64 | first get the values and then the names. | ||
65 | Fixed by Changelog entry: | ||
66 | |||
67 | 2004-09-12 Salvador E. Tropea <set@users.sf.net> | ||
68 | Andrew Cagney <cagney@gnu.org> | ||
69 | |||
70 | * mi/mi-main.c (mi_cmd_data_list_changed_registers) | ||
71 | (mi_cmd_data_list_register_values) | ||
72 | (mi_cmd_data_write_register_values): Include the PSEUDO_REGS in | ||
73 | the register number computation. | ||
74 | |||
75 | ***************************************************************************/ | ||
76 | |||
77 | #include "gdbmi.h" | ||
78 | |||
79 | /* Low level versions. */ | ||
80 | |||
81 | void mi_data_evaluate_expression(mi_h *h, const char *expression) | ||
82 | { | ||
83 | mi_send(h,"-data-evaluate-expression \"%s\"\n",expression); | ||
84 | } | ||
85 | |||
86 | void mi_dir(mi_h *h, const char *path) | ||
87 | { | ||
88 | if (h->version>=MI_VERSION2U(2,0,0)) | ||
89 | {// MI v2 | ||
90 | if (path) | ||
91 | mi_send(h,"-environment-directory \"%s\"\n",path); | ||
92 | else | ||
93 | mi_send(h,"-environment-directory -r\n"); | ||
94 | } | ||
95 | else | ||
96 | { | ||
97 | mi_send(h,"-environment-directory %s\n",path ? path : ""); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | void mi_data_read_memory_hx(mi_h *h, const char *exp, unsigned ws, | ||
102 | unsigned c, int convAddr) | ||
103 | { | ||
104 | if (convAddr) | ||
105 | mi_send(h,"-data-read-memory \"&%s\" x %d 1 %d\n",exp,ws,c); | ||
106 | else | ||
107 | mi_send(h,"-data-read-memory \"%s\" x %d 1 %d\n",exp,ws,c); | ||
108 | } | ||
109 | |||
110 | void mi_data_disassemble_se(mi_h *h, const char *start, const char *end, | ||
111 | int mode) | ||
112 | { | ||
113 | mi_send(h,"-data-disassemble -s \"%s\" -e \"%s\" -- %d\n",start,end,mode); | ||
114 | } | ||
115 | |||
116 | void mi_data_disassemble_fl(mi_h *h, const char *file, int line, int lines, | ||
117 | int mode) | ||
118 | { | ||
119 | mi_send(h,"-data-disassemble -f \"%s\" -l %d -n %d -- %d\n",file,line,lines, | ||
120 | mode); | ||
121 | } | ||
122 | |||
123 | void mi_data_list_register_names(mi_h *h) | ||
124 | { | ||
125 | mi_send(h,"-data-list-register-names\n"); | ||
126 | } | ||
127 | |||
128 | void mi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) | ||
129 | { | ||
130 | mi_send(h,"-data-list-register-names "); | ||
131 | while (l) | ||
132 | { | ||
133 | mi_send(h,"%d ",l->reg); | ||
134 | l=l->next; | ||
135 | } | ||
136 | mi_send(h,"\n"); | ||
137 | } | ||
138 | |||
139 | void mi_data_list_changed_registers(mi_h *h) | ||
140 | { | ||
141 | mi_send(h,"-data-list-changed-registers\n"); | ||
142 | } | ||
143 | |||
144 | void mi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) | ||
145 | { | ||
146 | mi_send(h,"-data-list-register-values %c ",mi_format_enum_to_char(fmt)); | ||
147 | while (l) | ||
148 | { | ||
149 | mi_send(h,"%d ",l->reg); | ||
150 | l=l->next; | ||
151 | } | ||
152 | mi_send(h,"\n"); | ||
153 | } | ||
154 | |||
155 | /* High level versions. */ | ||
156 | |||
157 | /**[txh]******************************************************************** | ||
158 | |||
159 | Description: | ||
160 | Evaluate an expression. Returns a parsed tree. | ||
161 | |||
162 | Command: -data-evaluate-expression | ||
163 | Return: The resulting value (as plain text) or NULL on error. | ||
164 | |||
165 | ***************************************************************************/ | ||
166 | |||
167 | char *gmi_data_evaluate_expression(mi_h *h, const char *expression) | ||
168 | { | ||
169 | mi_data_evaluate_expression(h,expression); | ||
170 | return mi_res_value(h); | ||
171 | } | ||
172 | |||
173 | /**[txh]******************************************************************** | ||
174 | |||
175 | Description: | ||
176 | Path for sources. You must use it to indicate where are the sources for | ||
177 | the program to debug. Only the MI v1 implementation is available. | ||
178 | |||
179 | Command: -environment-directory | ||
180 | Return: !=0 OK | ||
181 | |||
182 | ***************************************************************************/ | ||
183 | |||
184 | int gmi_dir(mi_h *h, const char *path) | ||
185 | { | ||
186 | mi_dir(h,path); | ||
187 | return mi_res_simple_done(h); | ||
188 | } | ||
189 | |||
190 | int gmi_read_memory(mi_h *h, const char *exp, unsigned size, | ||
191 | unsigned char *dest, int *na, int convAddr, | ||
192 | unsigned long *addr) | ||
193 | { | ||
194 | mi_data_read_memory_hx(h,exp,1,size,convAddr); | ||
195 | return mi_get_read_memory(h,dest,1,na,addr); | ||
196 | } | ||
197 | |||
198 | mi_asm_insns *gmi_data_disassemble_se(mi_h *h, const char *start, | ||
199 | const char *end, int mode) | ||
200 | { | ||
201 | mi_data_disassemble_se(h,start,end,mode); | ||
202 | return mi_get_asm_insns(h); | ||
203 | } | ||
204 | |||
205 | mi_asm_insns *gmi_data_disassemble_fl(mi_h *h, const char *file, int line, | ||
206 | int lines, int mode) | ||
207 | { | ||
208 | mi_data_disassemble_fl(h,file,line,lines,mode); | ||
209 | return mi_get_asm_insns(h); | ||
210 | } | ||
211 | |||
212 | // Affected by gdb bug mi/1770 | ||
213 | mi_chg_reg *gmi_data_list_register_names(mi_h *h, int *how_many) | ||
214 | { | ||
215 | mi_data_list_register_names(h); | ||
216 | return mi_get_list_registers(h,how_many); | ||
217 | } | ||
218 | |||
219 | int gmi_data_list_register_names_l(mi_h *h, mi_chg_reg *l) | ||
220 | { | ||
221 | mi_data_list_register_names_l(h,l); | ||
222 | return mi_get_list_registers_l(h,l); | ||
223 | } | ||
224 | |||
225 | mi_chg_reg *gmi_data_list_changed_registers(mi_h *h) | ||
226 | { | ||
227 | mi_error=MI_OK; | ||
228 | mi_data_list_changed_registers(h); | ||
229 | return mi_get_list_changed_regs(h); | ||
230 | } | ||
231 | |||
232 | int gmi_data_list_register_values(mi_h *h, enum mi_gvar_fmt fmt, mi_chg_reg *l) | ||
233 | { | ||
234 | mi_data_list_register_values(h,fmt,l); | ||
235 | return mi_get_reg_values(h,l); | ||
236 | } | ||
237 | |||
238 | mi_chg_reg *gmi_data_list_all_register_values(mi_h *h, enum mi_gvar_fmt fmt, int *how_many) | ||
239 | { | ||
240 | mi_data_list_register_values(h,fmt,NULL); | ||
241 | return mi_get_reg_values_l(h,how_many); | ||
242 | } | ||
243 | |||
diff --git a/pathologist/src/mi/gdbmi_error.c b/pathologist/src/mi/gdbmi_error.c new file mode 100644 index 0000000..abb20f6 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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/pathologist/src/mi/gdbmi_get_free_pty.c b/pathologist/src/mi/gdbmi_get_free_pty.c new file mode 100644 index 0000000..4274c78 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.h" | ||
32 | |||
33 | /**[txh]******************************************************************** | ||
34 | |||
35 | Description: | ||
36 | Look for a free and usable pseudo terminal. Low level, use | ||
37 | 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 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/pathologist/src/mi/gdbmi_get_free_vt.c b/pathologist/src/mi/gdbmi_get_free_vt.c new file mode 100644 index 0000000..25e5e75 --- /dev/null +++ b/pathologist/src/mi/gdbmi_get_free_vt.c | |||
@@ -0,0 +1,156 @@ | |||
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 "gdbmi.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 | close(console_fd); | ||
97 | return -2; | ||
98 | } | ||
99 | close(console_fd); | ||
100 | |||
101 | /* We attempt to set our euid to 0; if we were run with euid 0 to | ||
102 | * start with, we'll be able to do this now. Otherwise, we'll just | ||
103 | * ignore the error returned since it might not be a problem if the | ||
104 | * ttys we look at are owned by the user running the program. */ | ||
105 | seteuid(0); | ||
106 | |||
107 | /* tty0 is not really a console, so start counting at 2. */ | ||
108 | fd=-1; | ||
109 | for (tty=1, mask=2; mask; tty++, mask<<=1) | ||
110 | if (!(vts.v_state & mask)) | ||
111 | { | ||
112 | snprintf(tty_name,sizeof(tty_name),"/dev/tty%d",tty); | ||
113 | fd=open(tty_name,O_RDWR); | ||
114 | if (fd!=-1) | ||
115 | { | ||
116 | close(fd); | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | seteuid(getuid()); | ||
122 | |||
123 | if (!mask) | ||
124 | return -3; | ||
125 | |||
126 | return tty; | ||
127 | } | ||
128 | |||
129 | /**[txh]******************************************************************** | ||
130 | |||
131 | Description: | ||
132 | Look for a free and usable Linux VT to be used by the child. | ||
133 | |||
134 | Return: A new mi_aux_term structure, you can use @x{gmi_end_aux_term} to | ||
135 | release it. | ||
136 | |||
137 | ***************************************************************************/ | ||
138 | |||
139 | mi_aux_term *gmi_look_for_free_vt() | ||
140 | { | ||
141 | int ret; | ||
142 | int vt=mi_look_for_free_vt(); | ||
143 | mi_aux_term *res; | ||
144 | |||
145 | if (vt<0) | ||
146 | return NULL; | ||
147 | res=(mi_aux_term *)malloc(sizeof(mi_aux_term)); | ||
148 | if (!res) | ||
149 | return NULL; | ||
150 | res->pid=-1; | ||
151 | ret = asprintf(&res->tty,"/dev/tty%d",vt); | ||
152 | return res; | ||
153 | } | ||
154 | |||
155 | #endif | ||
156 | |||
diff --git a/pathologist/src/mi/gdbmi_misc.c b/pathologist/src/mi/gdbmi_misc.c new file mode 100644 index 0000000..51088a7 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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/pathologist/src/mi/gdbmi_parse.c b/pathologist/src/mi/gdbmi_parse.c new file mode 100644 index 0000000..d30bd22 --- /dev/null +++ b/pathologist/src/mi/gdbmi_parse.c | |||
@@ -0,0 +1,1927 @@ | |||
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 "gdbmi.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 | mi_error = MI_PARSER; | ||
55 | return 0; | ||
56 | } | ||
57 | if (*s=='\\') | ||
58 | s++; | ||
59 | len++; | ||
60 | } | ||
61 | /* Copy. */ | ||
62 | r->type=t_const; | ||
63 | d=r->v.cstr=mi_malloc(len+1); | ||
64 | if (!r->v.cstr) | ||
65 | return 0; | ||
66 | for (s=str; *s && !EndOfStr(s); s++, d++) | ||
67 | { | ||
68 | if (*s=='\\') | ||
69 | { | ||
70 | s++; | ||
71 | switch (*s) | ||
72 | { | ||
73 | case 'n': | ||
74 | *d='\n'; | ||
75 | break; | ||
76 | case 't': | ||
77 | *d='\t'; | ||
78 | break; | ||
79 | default: | ||
80 | *d=*s; | ||
81 | } | ||
82 | } | ||
83 | else | ||
84 | *d=*s; | ||
85 | } | ||
86 | *d=0; | ||
87 | if (end) | ||
88 | *end=s+1; | ||
89 | |||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | /* TODO: What's a valid variable name? | ||
94 | I'll assume a-zA-Z0-9_- */ | ||
95 | //inline | ||
96 | int mi_is_var_name_char(char c) | ||
97 | { | ||
98 | return isalnum(c) || c=='-' || c=='_'; | ||
99 | } | ||
100 | |||
101 | char *mi_get_var_name(const char *str, const char **end) | ||
102 | { | ||
103 | const char *s; | ||
104 | char *r; | ||
105 | int l; | ||
106 | /* Meassure. */ | ||
107 | for (s=str; *s && mi_is_var_name_char(*s); s++); | ||
108 | if (*s!='=') | ||
109 | { | ||
110 | mi_error=MI_PARSER; | ||
111 | return NULL; | ||
112 | } | ||
113 | /* Allocate. */ | ||
114 | l=s-str; | ||
115 | r=mi_malloc(l+1); | ||
116 | /* Copy. */ | ||
117 | if (NULL != r) { | ||
118 | memcpy(r,str,l); | ||
119 | r[l]=0; | ||
120 | } | ||
121 | if (end) | ||
122 | *end=s+1; | ||
123 | return r; | ||
124 | } | ||
125 | |||
126 | |||
127 | int mi_get_list_res(mi_results *r, const char *str, const char **end, char closeC) | ||
128 | { | ||
129 | mi_results *last_r, *rs; | ||
130 | |||
131 | last_r=NULL; | ||
132 | do | ||
133 | { | ||
134 | rs=mi_get_result(str,&str); | ||
135 | if (last_r) | ||
136 | last_r->next=rs; | ||
137 | else | ||
138 | r->v.rs=rs; | ||
139 | last_r=rs; | ||
140 | if (*str==closeC) | ||
141 | { | ||
142 | *end=str+1; | ||
143 | return 1; | ||
144 | } | ||
145 | if (*str!=',') | ||
146 | break; | ||
147 | str++; | ||
148 | } | ||
149 | while (1); | ||
150 | |||
151 | mi_error=MI_PARSER; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | #ifdef __APPLE__ | ||
156 | int mi_get_tuple_val(mi_results *r, const char *str, const char **end) | ||
157 | { | ||
158 | mi_results *last_r, *rs; | ||
159 | |||
160 | last_r=NULL; | ||
161 | do | ||
162 | { | ||
163 | rs=mi_alloc_results(); | ||
164 | if (!rs || !mi_get_value(rs,str,&str)) | ||
165 | { | ||
166 | mi_free_results(rs); | ||
167 | return 0; | ||
168 | } | ||
169 | /* Note that rs->var is NULL, that indicates that's just a value and not | ||
170 | a result. */ | ||
171 | if (last_r) | ||
172 | last_r->next=rs; | ||
173 | else | ||
174 | r->v.rs=rs; | ||
175 | last_r=rs; | ||
176 | if (*str=='}') | ||
177 | { | ||
178 | *end=str+1; | ||
179 | return 1; | ||
180 | } | ||
181 | if (*str!=',') | ||
182 | break; | ||
183 | str++; | ||
184 | } | ||
185 | while (1); | ||
186 | |||
187 | mi_error=MI_PARSER; | ||
188 | return 0; | ||
189 | } | ||
190 | #endif /* __APPLE__ */ | ||
191 | |||
192 | int mi_get_tuple(mi_results *r, const char *str, const char **end) | ||
193 | { | ||
194 | if (*str!='{') | ||
195 | { | ||
196 | mi_error=MI_PARSER; | ||
197 | return 0; | ||
198 | } | ||
199 | r->type=t_tuple; | ||
200 | str++; | ||
201 | if (*str=='}') | ||
202 | {/* Special case: empty tuple */ | ||
203 | *end=str+1; | ||
204 | return 1; | ||
205 | } | ||
206 | #ifdef __APPLE__ | ||
207 | if (mi_is_var_name_char(*str)) | ||
208 | return mi_get_list_res(r,str,end,'}'); | ||
209 | return mi_get_tuple_val(r,str,end); | ||
210 | #else /* __APPLE__ */ | ||
211 | return mi_get_list_res(r,str,end,'}'); | ||
212 | #endif /* __APPLE__ */ | ||
213 | } | ||
214 | |||
215 | int mi_get_list_val(mi_results *r, const char *str, const char **end) | ||
216 | { | ||
217 | mi_results *last_r, *rs; | ||
218 | |||
219 | last_r=NULL; | ||
220 | do | ||
221 | { | ||
222 | rs=mi_alloc_results(); | ||
223 | if (!rs || !mi_get_value(rs,str,&str)) | ||
224 | { | ||
225 | mi_free_results(rs); | ||
226 | return 0; | ||
227 | } | ||
228 | /* Note that rs->var is NULL, that indicates that's just a value and not | ||
229 | a result. */ | ||
230 | if (last_r) | ||
231 | last_r->next=rs; | ||
232 | else | ||
233 | r->v.rs=rs; | ||
234 | last_r=rs; | ||
235 | if (*str==']') | ||
236 | { | ||
237 | *end=str+1; | ||
238 | return 1; | ||
239 | } | ||
240 | if (*str!=',') | ||
241 | break; | ||
242 | str++; | ||
243 | } | ||
244 | while (1); | ||
245 | |||
246 | mi_error=MI_PARSER; | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | int mi_get_list(mi_results *r, const char *str, const char **end) | ||
251 | { | ||
252 | if (*str!='[') | ||
253 | { | ||
254 | mi_error=MI_PARSER; | ||
255 | return 0; | ||
256 | } | ||
257 | r->type=t_list; | ||
258 | str++; | ||
259 | if (*str==']') | ||
260 | {/* Special case: empty list */ | ||
261 | *end=str+1; | ||
262 | return 1; | ||
263 | } | ||
264 | /* Comment: I think they could choose () for values. Is confusing in this way. */ | ||
265 | if (mi_is_var_name_char(*str)) | ||
266 | return mi_get_list_res(r,str,end,']'); | ||
267 | return mi_get_list_val(r,str,end); | ||
268 | } | ||
269 | |||
270 | int mi_get_value(mi_results *r, const char *str, const char **end) | ||
271 | { | ||
272 | switch (str[0]) | ||
273 | { | ||
274 | case '"': | ||
275 | return mi_get_cstring_r(r,str,end); | ||
276 | case '{': | ||
277 | return mi_get_tuple(r,str,end); | ||
278 | case '[': | ||
279 | return mi_get_list(r,str,end); | ||
280 | } | ||
281 | mi_error=MI_PARSER; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | mi_results *mi_get_result(const char *str, const char **end) | ||
286 | { | ||
287 | char *var; | ||
288 | mi_results *r; | ||
289 | |||
290 | var=mi_get_var_name(str,&str); | ||
291 | if (!var) | ||
292 | return NULL; | ||
293 | |||
294 | r=mi_alloc_results(); | ||
295 | if (!r) | ||
296 | { | ||
297 | free(var); | ||
298 | return NULL; | ||
299 | } | ||
300 | r->var=var; | ||
301 | |||
302 | if (!mi_get_value(r,str,end)) | ||
303 | { | ||
304 | mi_free_results(r); | ||
305 | return NULL; | ||
306 | } | ||
307 | |||
308 | return r; | ||
309 | } | ||
310 | |||
311 | mi_output *mi_get_results_alone(mi_output *r,const char *str) | ||
312 | { | ||
313 | mi_results *last_r, *rs; | ||
314 | |||
315 | /* * results */ | ||
316 | last_r=NULL; | ||
317 | do | ||
318 | { | ||
319 | if (!*str) | ||
320 | return r; | ||
321 | if (*str!=',') | ||
322 | { | ||
323 | mi_error=MI_PARSER; | ||
324 | break; | ||
325 | } | ||
326 | str++; | ||
327 | rs=mi_get_result(str,&str); | ||
328 | if (!rs) | ||
329 | break; | ||
330 | if (!last_r) | ||
331 | r->c=rs; | ||
332 | else | ||
333 | last_r->next=rs; | ||
334 | last_r=rs; | ||
335 | } | ||
336 | while (1); | ||
337 | mi_free_output(r); | ||
338 | return NULL; | ||
339 | } | ||
340 | |||
341 | mi_output *mi_parse_result_record(mi_output *r,const char *str) | ||
342 | { | ||
343 | r->type=MI_T_RESULT_RECORD; | ||
344 | |||
345 | /* Solve the result-class. */ | ||
346 | if (strncmp(str,"done",4)==0) | ||
347 | { | ||
348 | str+=4; | ||
349 | r->tclass=MI_CL_DONE; | ||
350 | } | ||
351 | else if (strncmp(str,"running",7)==0) | ||
352 | { | ||
353 | str+=7; | ||
354 | r->tclass=MI_CL_RUNNING; | ||
355 | } | ||
356 | else if (strncmp(str,"connected",9)==0) | ||
357 | { | ||
358 | str+=9; | ||
359 | r->tclass=MI_CL_CONNECTED; | ||
360 | } | ||
361 | else if (strncmp(str,"error",5)==0) | ||
362 | { | ||
363 | str+=5; | ||
364 | r->tclass=MI_CL_ERROR; | ||
365 | } | ||
366 | else if (strncmp(str,"exit",4)==0) | ||
367 | { | ||
368 | str+=4; | ||
369 | r->tclass=MI_CL_EXIT; | ||
370 | } | ||
371 | else | ||
372 | { | ||
373 | mi_error=MI_UNKNOWN_RESULT; | ||
374 | return NULL; | ||
375 | } | ||
376 | |||
377 | return mi_get_results_alone(r,str); | ||
378 | } | ||
379 | |||
380 | mi_output *mi_parse_asyn(mi_output *r,const char *str) | ||
381 | { | ||
382 | r->type=MI_T_OUT_OF_BAND; | ||
383 | r->stype=MI_ST_ASYNC; | ||
384 | /* async-class. */ | ||
385 | if (strncmp(str,"stopped",7)==0) | ||
386 | { | ||
387 | r->tclass=MI_CL_STOPPED; | ||
388 | str+=7; | ||
389 | return mi_get_results_alone(r,str); | ||
390 | } | ||
391 | if (strncmp(str,"download",8)==0) | ||
392 | { | ||
393 | r->tclass=MI_CL_DOWNLOAD; | ||
394 | str+=8; | ||
395 | return mi_get_results_alone(r,str); | ||
396 | } | ||
397 | mi_error=MI_UNKNOWN_ASYNC; | ||
398 | mi_free_output(r); | ||
399 | return NULL; | ||
400 | } | ||
401 | |||
402 | mi_output *mi_parse_exec_asyn(mi_output *r,const char *str) | ||
403 | { | ||
404 | r->sstype=MI_SST_EXEC; | ||
405 | return mi_parse_asyn(r,str); | ||
406 | } | ||
407 | |||
408 | mi_output *mi_parse_status_asyn(mi_output *r,const char *str) | ||
409 | { | ||
410 | r->sstype=MI_SST_STATUS; | ||
411 | return mi_parse_asyn(r,str); | ||
412 | } | ||
413 | |||
414 | mi_output *mi_parse_notify_asyn(mi_output *r,const char *str) | ||
415 | { | ||
416 | r->sstype=MI_SST_NOTIFY; | ||
417 | return mi_parse_asyn(r,str); | ||
418 | } | ||
419 | |||
420 | mi_output *mi_console(mi_output *r,const char *str) | ||
421 | { | ||
422 | r->type=MI_T_OUT_OF_BAND; | ||
423 | r->stype=MI_ST_STREAM; | ||
424 | r->c=mi_alloc_results(); | ||
425 | if (!r->c || !mi_get_cstring_r(r->c,str,NULL)) | ||
426 | { | ||
427 | mi_free_output(r); | ||
428 | return NULL; | ||
429 | } | ||
430 | return r; | ||
431 | } | ||
432 | |||
433 | mi_output *mi_console_stream(mi_output *r,const char *str) | ||
434 | { | ||
435 | r->sstype=MI_SST_CONSOLE; | ||
436 | return mi_console(r,str); | ||
437 | } | ||
438 | |||
439 | mi_output *mi_target_stream(mi_output *r,const char *str) | ||
440 | { | ||
441 | r->sstype=MI_SST_TARGET; | ||
442 | return mi_console(r,str); | ||
443 | } | ||
444 | |||
445 | mi_output *mi_log_stream(mi_output *r,const char *str) | ||
446 | { | ||
447 | r->sstype=MI_SST_LOG; | ||
448 | return mi_console(r,str); | ||
449 | } | ||
450 | |||
451 | mi_output *mi_parse_gdb_output(const char *str) | ||
452 | { | ||
453 | char type=str[0]; | ||
454 | |||
455 | mi_output *r=mi_alloc_output(); | ||
456 | if (!r) | ||
457 | { | ||
458 | mi_error=MI_OUT_OF_MEMORY; | ||
459 | return NULL; | ||
460 | } | ||
461 | str++; | ||
462 | switch (type) | ||
463 | { | ||
464 | case '^': | ||
465 | return mi_parse_result_record(r,str); | ||
466 | case '*': | ||
467 | return mi_parse_exec_asyn(r,str); | ||
468 | case '+': | ||
469 | return mi_parse_status_asyn(r,str); | ||
470 | case '=': | ||
471 | return mi_parse_notify_asyn(r,str); | ||
472 | case '~': | ||
473 | return mi_console_stream(r,str); | ||
474 | case '@': | ||
475 | return mi_target_stream(r,str); | ||
476 | case '&': | ||
477 | return mi_log_stream(r,str); | ||
478 | } | ||
479 | mi_error=MI_PARSER; | ||
480 | return NULL; | ||
481 | } | ||
482 | |||
483 | mi_output *mi_get_rrecord(mi_output *r) | ||
484 | { | ||
485 | if (!r) | ||
486 | return NULL; | ||
487 | while (r) | ||
488 | { | ||
489 | if (r->type==MI_T_RESULT_RECORD) | ||
490 | return r; | ||
491 | r=r->next; | ||
492 | } | ||
493 | return r; | ||
494 | } | ||
495 | |||
496 | mi_results *mi_get_var_r(mi_results *r, const char *var) | ||
497 | { | ||
498 | while (r) | ||
499 | { | ||
500 | if (strcmp(r->var,var)==0) | ||
501 | return r; | ||
502 | r=r->next; | ||
503 | } | ||
504 | return NULL; | ||
505 | } | ||
506 | |||
507 | mi_results *mi_get_var(mi_output *res, const char *var) | ||
508 | { | ||
509 | if (!res) | ||
510 | return NULL; | ||
511 | return mi_get_var_r(res->c,var); | ||
512 | } | ||
513 | |||
514 | int mi_get_async_stop_reason(mi_output *r, char **reason) | ||
515 | { | ||
516 | int found_stopped=0; | ||
517 | |||
518 | *reason=NULL; | ||
519 | while (r) | ||
520 | { | ||
521 | if (r->type==MI_T_RESULT_RECORD && r->tclass==MI_CL_ERROR) | ||
522 | { | ||
523 | if (r->c->type==t_const) | ||
524 | *reason=r->c->v.cstr; | ||
525 | return 0; | ||
526 | } | ||
527 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
528 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
529 | { | ||
530 | mi_results *p=r->c; | ||
531 | found_stopped=1; | ||
532 | while (p) | ||
533 | { | ||
534 | if (strcmp(p->var,"reason")==0) | ||
535 | { | ||
536 | *reason=p->v.cstr; | ||
537 | return 1; | ||
538 | } | ||
539 | p=p->next; | ||
540 | } | ||
541 | } | ||
542 | r=r->next; | ||
543 | } | ||
544 | if (*reason==NULL && found_stopped) | ||
545 | { | ||
546 | *reason=strdup("unknown (temp bkpt?)"); | ||
547 | return 1; | ||
548 | } | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | mi_frames *mi_get_async_frame(mi_output *r) | ||
553 | { | ||
554 | while (r) | ||
555 | { | ||
556 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
557 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
558 | { | ||
559 | mi_results *p=r->c; | ||
560 | while (p) | ||
561 | { | ||
562 | if (strcmp(p->var,"frame")==0) | ||
563 | return mi_parse_frame(p->v.rs); | ||
564 | p=p->next; | ||
565 | } | ||
566 | } | ||
567 | r=r->next; | ||
568 | } | ||
569 | return NULL; | ||
570 | } | ||
571 | |||
572 | int mi_res_simple(mi_h *h, int tclass, int accert_ret) | ||
573 | { | ||
574 | mi_output *r, *res; | ||
575 | int ret=0; | ||
576 | |||
577 | r=mi_get_response_blk(h); | ||
578 | res=mi_get_rrecord(r); | ||
579 | |||
580 | if (res) | ||
581 | ret=res->tclass==tclass; | ||
582 | mi_free_output(r); | ||
583 | |||
584 | return ret; | ||
585 | } | ||
586 | |||
587 | |||
588 | int mi_res_simple_done(mi_h *h) | ||
589 | { | ||
590 | return mi_res_simple(h,MI_CL_DONE,0); | ||
591 | } | ||
592 | |||
593 | int mi_res_simple_exit(mi_h *h) | ||
594 | { | ||
595 | return mi_res_simple(h,MI_CL_EXIT,1); | ||
596 | } | ||
597 | |||
598 | int mi_res_simple_running(mi_h *h) | ||
599 | { | ||
600 | return mi_res_simple(h,MI_CL_RUNNING,0); | ||
601 | } | ||
602 | |||
603 | int mi_res_simple_connected(mi_h *h) | ||
604 | { | ||
605 | return mi_res_simple(h,MI_CL_CONNECTED,0); | ||
606 | } | ||
607 | |||
608 | mi_results *mi_res_var(mi_h *h, const char *var, int tclass) | ||
609 | { | ||
610 | mi_output *r, *res; | ||
611 | mi_results *the_var=NULL; | ||
612 | |||
613 | r=mi_get_response_blk(h); | ||
614 | /* All the code that follows is "NULL" tolerant. */ | ||
615 | /* Look for the result-record. */ | ||
616 | res=mi_get_rrecord(r); | ||
617 | /* Look for the desired var. */ | ||
618 | if (res && res->tclass==tclass) | ||
619 | the_var=mi_get_var(res,var); | ||
620 | /* Release all but the one we want. */ | ||
621 | mi_free_output_but(r,NULL,the_var); | ||
622 | return the_var; | ||
623 | } | ||
624 | |||
625 | mi_results *mi_res_done_var(mi_h *h, const char *var) | ||
626 | { | ||
627 | return mi_res_var(h,var,MI_CL_DONE); | ||
628 | } | ||
629 | |||
630 | mi_frames *mi_parse_frame(mi_results *c) | ||
631 | { | ||
632 | mi_frames *res=mi_alloc_frames(); | ||
633 | char *end; | ||
634 | |||
635 | if (res) | ||
636 | { | ||
637 | while (c) | ||
638 | { | ||
639 | if (c->type==t_const) | ||
640 | { | ||
641 | if (strcmp(c->var,"level")==0) | ||
642 | res->level=atoi(c->v.cstr); | ||
643 | else if (strcmp(c->var,"addr")==0) | ||
644 | res->addr=(void *)strtoul(c->v.cstr,&end,0); | ||
645 | else if (strcmp(c->var,"func")==0) | ||
646 | { | ||
647 | res->func=c->v.cstr; | ||
648 | c->v.cstr=NULL; | ||
649 | } | ||
650 | else if (strcmp(c->var,"file")==0) | ||
651 | { | ||
652 | res->file=c->v.cstr; | ||
653 | c->v.cstr=NULL; | ||
654 | } | ||
655 | else if (strcmp(c->var,"from")==0) | ||
656 | { | ||
657 | res->from=c->v.cstr; | ||
658 | c->v.cstr=NULL; | ||
659 | } | ||
660 | else if (strcmp(c->var,"line")==0) | ||
661 | res->line=atoi(c->v.cstr); | ||
662 | } | ||
663 | else if (c->type==t_list && strcmp(c->var,"args")==0) | ||
664 | { | ||
665 | res->args=c->v.rs; | ||
666 | c->v.rs=NULL; | ||
667 | } | ||
668 | c=c->next; | ||
669 | } | ||
670 | } | ||
671 | return res; | ||
672 | } | ||
673 | |||
674 | mi_frames *mi_res_frame(mi_h *h) | ||
675 | { | ||
676 | mi_results *r=mi_res_done_var(h,"frame"); | ||
677 | mi_frames *f=NULL; | ||
678 | |||
679 | if (r && r->type==t_tuple) | ||
680 | f=mi_parse_frame(r->v.rs); | ||
681 | mi_free_results(r); | ||
682 | return f; | ||
683 | } | ||
684 | |||
685 | mi_frames *mi_res_frames_array(mi_h *h, const char *var) | ||
686 | { | ||
687 | mi_results *r=mi_res_done_var(h,var), *c; | ||
688 | mi_frames *res=NULL, *nframe, *last=NULL; | ||
689 | |||
690 | if (!r) | ||
691 | return NULL; | ||
692 | #ifdef __APPLE__ | ||
693 | if (r->type!=t_list && r->type!=t_tuple) | ||
694 | #else | ||
695 | if (r->type!=t_list) | ||
696 | #endif | ||
697 | { | ||
698 | mi_free_results(r); | ||
699 | return NULL; | ||
700 | } | ||
701 | c=r->v.rs; | ||
702 | while (c) | ||
703 | { | ||
704 | if (strcmp(c->var,"frame")==0 && c->type==t_tuple) | ||
705 | { | ||
706 | nframe=mi_parse_frame(c->v.rs); | ||
707 | if (nframe) | ||
708 | { | ||
709 | if (!last) | ||
710 | res=nframe; | ||
711 | else | ||
712 | last->next=nframe; | ||
713 | last=nframe; | ||
714 | } | ||
715 | } | ||
716 | c=c->next; | ||
717 | } | ||
718 | mi_free_results(r); | ||
719 | return res; | ||
720 | } | ||
721 | |||
722 | mi_frames *mi_res_frames_list(mi_h *h) | ||
723 | { | ||
724 | mi_output *r, *res; | ||
725 | mi_frames *ret=NULL, *nframe, *last=NULL; | ||
726 | mi_results *c; | ||
727 | |||
728 | r=mi_get_response_blk(h); | ||
729 | res=mi_get_rrecord(r); | ||
730 | if (res && res->tclass==MI_CL_DONE) | ||
731 | { | ||
732 | c=res->c; | ||
733 | while (c) | ||
734 | { | ||
735 | if (strcmp(c->var,"frame")==0 && c->type==t_tuple) | ||
736 | { | ||
737 | nframe=mi_parse_frame(c->v.rs); | ||
738 | if (nframe) | ||
739 | { | ||
740 | if (!last) | ||
741 | ret=nframe; | ||
742 | else | ||
743 | last->next=nframe; | ||
744 | last=nframe; | ||
745 | } | ||
746 | } | ||
747 | c=c->next; | ||
748 | } | ||
749 | } | ||
750 | mi_free_output(r); | ||
751 | return ret; | ||
752 | } | ||
753 | |||
754 | int mi_get_thread_ids(mi_output *res, int **list) | ||
755 | { | ||
756 | mi_results *vids, *lids; | ||
757 | int ids=-1, i; | ||
758 | |||
759 | *list=NULL; | ||
760 | vids=mi_get_var(res,"number-of-threads"); | ||
761 | lids=mi_get_var(res,"thread-ids"); | ||
762 | if (vids && vids->type==t_const && | ||
763 | lids && lids->type==t_tuple) | ||
764 | { | ||
765 | ids=atoi(vids->v.cstr); | ||
766 | if (ids) | ||
767 | { | ||
768 | int *lst; | ||
769 | lst=(int *)mi_calloc(ids,sizeof(int)); | ||
770 | if (lst) | ||
771 | { | ||
772 | lids=lids->v.rs; | ||
773 | i=0; | ||
774 | while (lids) | ||
775 | { | ||
776 | if (strcmp(lids->var,"thread-id")==0 && lids->type==t_const) | ||
777 | lst[i++]=atoi(lids->v.cstr); | ||
778 | lids=lids->next; | ||
779 | } | ||
780 | *list=lst; | ||
781 | } | ||
782 | else | ||
783 | ids=-1; | ||
784 | } | ||
785 | } | ||
786 | return ids; | ||
787 | } | ||
788 | |||
789 | int mi_res_thread_ids(mi_h *h, int **list) | ||
790 | { | ||
791 | mi_output *r, *res; | ||
792 | int ids=-1; | ||
793 | |||
794 | r=mi_get_response_blk(h); | ||
795 | res=mi_get_rrecord(r); | ||
796 | if (res && res->tclass==MI_CL_DONE) | ||
797 | ids=mi_get_thread_ids(res,list); | ||
798 | mi_free_output(r); | ||
799 | return ids; | ||
800 | } | ||
801 | |||
802 | enum mi_gvar_lang mi_lang_str_to_enum(const char *lang) | ||
803 | { | ||
804 | enum mi_gvar_lang lg=lg_unknown; | ||
805 | |||
806 | if (strcmp(lang,"C")==0) | ||
807 | lg=lg_c; | ||
808 | else if (strcmp(lang,"C++")==0) | ||
809 | lg=lg_cpp; | ||
810 | else if (strcmp(lang,"Java")==0) | ||
811 | lg=lg_java; | ||
812 | |||
813 | return lg; | ||
814 | } | ||
815 | |||
816 | const char *mi_lang_enum_to_str(enum mi_gvar_lang lang) | ||
817 | { | ||
818 | const char *lg; | ||
819 | |||
820 | switch (lang) | ||
821 | { | ||
822 | case lg_c: | ||
823 | lg="C"; | ||
824 | break; | ||
825 | case lg_cpp: | ||
826 | lg="C++"; | ||
827 | break; | ||
828 | case lg_java: | ||
829 | lg="Java"; | ||
830 | break; | ||
831 | /*case lg_unknown:*/ | ||
832 | default: | ||
833 | lg="unknown"; | ||
834 | break; | ||
835 | } | ||
836 | return lg; | ||
837 | } | ||
838 | |||
839 | enum mi_gvar_fmt mi_format_str_to_enum(const char *format) | ||
840 | { | ||
841 | enum mi_gvar_fmt fmt=fm_natural; | ||
842 | |||
843 | if (strcmp(format,"binary")==0) | ||
844 | fmt=fm_binary; | ||
845 | else if (strcmp(format,"decimal")==0) | ||
846 | fmt=fm_decimal; | ||
847 | else if (strcmp(format,"hexadecimal")==0) | ||
848 | fmt=fm_hexadecimal; | ||
849 | else if (strcmp(format,"octal")==0) | ||
850 | fmt=fm_octal; | ||
851 | |||
852 | return fmt; | ||
853 | } | ||
854 | |||
855 | const char *mi_format_enum_to_str(enum mi_gvar_fmt format) | ||
856 | { | ||
857 | const char *fmt; | ||
858 | |||
859 | switch (format) | ||
860 | { | ||
861 | case fm_natural: | ||
862 | fmt="natural"; | ||
863 | break; | ||
864 | case fm_binary: | ||
865 | fmt="binary"; | ||
866 | break; | ||
867 | case fm_decimal: | ||
868 | fmt="decimal"; | ||
869 | break; | ||
870 | case fm_hexadecimal: | ||
871 | fmt="hexadecimal"; | ||
872 | break; | ||
873 | case fm_octal: | ||
874 | fmt="octal"; | ||
875 | break; | ||
876 | case fm_raw: | ||
877 | fmt="raw"; | ||
878 | break; | ||
879 | default: | ||
880 | fmt="unknown"; | ||
881 | } | ||
882 | return fmt; | ||
883 | } | ||
884 | |||
885 | char mi_format_enum_to_char(enum mi_gvar_fmt format) | ||
886 | { | ||
887 | char fmt; | ||
888 | |||
889 | switch (format) | ||
890 | { | ||
891 | case fm_natural: | ||
892 | fmt='N'; | ||
893 | break; | ||
894 | case fm_binary: | ||
895 | fmt='t'; | ||
896 | break; | ||
897 | case fm_decimal: | ||
898 | fmt='d'; | ||
899 | break; | ||
900 | case fm_hexadecimal: | ||
901 | fmt='x'; | ||
902 | break; | ||
903 | case fm_octal: | ||
904 | fmt='o'; | ||
905 | break; | ||
906 | case fm_raw: | ||
907 | fmt='r'; | ||
908 | break; | ||
909 | default: | ||
910 | fmt=' '; | ||
911 | } | ||
912 | return fmt; | ||
913 | } | ||
914 | |||
915 | mi_gvar *mi_get_gvar(mi_output *o, mi_gvar *cur, const char *expression) | ||
916 | { | ||
917 | mi_results *r; | ||
918 | mi_gvar *res=cur ? cur : mi_alloc_gvar(); | ||
919 | int l; | ||
920 | |||
921 | if (!res) | ||
922 | return res; | ||
923 | r=o->c; | ||
924 | if (expression) | ||
925 | res->exp=strdup(expression); | ||
926 | while (r) | ||
927 | { | ||
928 | if (r->type==t_const) | ||
929 | { | ||
930 | if (strcmp(r->var,"name")==0) | ||
931 | { | ||
932 | free(res->name); | ||
933 | res->name=r->v.cstr; | ||
934 | r->v.cstr=NULL; | ||
935 | } | ||
936 | else if (strcmp(r->var,"numchild")==0) | ||
937 | { | ||
938 | res->numchild=atoi(r->v.cstr); | ||
939 | } | ||
940 | else if (strcmp(r->var,"type")==0) | ||
941 | { | ||
942 | free(res->type); | ||
943 | res->type=r->v.cstr; | ||
944 | r->v.cstr=NULL; | ||
945 | l=strlen(res->type); | ||
946 | if (l && res->type[l-1]=='*') | ||
947 | res->ispointer=1; | ||
948 | } | ||
949 | else if (strcmp(r->var,"lang")==0) | ||
950 | { | ||
951 | res->lang=mi_lang_str_to_enum(r->v.cstr); | ||
952 | } | ||
953 | else if (strcmp(r->var,"exp")==0) | ||
954 | { | ||
955 | free(res->exp); | ||
956 | res->exp=r->v.cstr; | ||
957 | r->v.cstr=NULL; | ||
958 | } | ||
959 | else if (strcmp(r->var,"format")==0) | ||
960 | { | ||
961 | res->format=mi_format_str_to_enum(r->v.cstr); | ||
962 | } | ||
963 | else if (strcmp(r->var,"attr")==0) | ||
964 | { /* Note: gdb 6.1.1 have only this: */ | ||
965 | if (strcmp(r->v.cstr,"editable")==0) | ||
966 | res->attr=MI_ATTR_EDITABLE; | ||
967 | else /* noneditable */ | ||
968 | res->attr=MI_ATTR_NONEDITABLE; | ||
969 | } | ||
970 | } | ||
971 | r=r->next; | ||
972 | } | ||
973 | return res; | ||
974 | } | ||
975 | |||
976 | mi_gvar *mi_res_gvar(mi_h *h, mi_gvar *cur, const char *expression) | ||
977 | { | ||
978 | mi_output *r, *res; | ||
979 | mi_gvar *gvar=NULL; | ||
980 | |||
981 | r=mi_get_response_blk(h); | ||
982 | res=mi_get_rrecord(r); | ||
983 | if (res && res->tclass==MI_CL_DONE) | ||
984 | gvar=mi_get_gvar(res,cur,expression); | ||
985 | mi_free_output(r); | ||
986 | return gvar; | ||
987 | } | ||
988 | |||
989 | mi_gvar_chg *mi_get_gvar_chg(mi_results *r) | ||
990 | { | ||
991 | mi_gvar_chg *n; | ||
992 | |||
993 | if (r->type!=t_const) | ||
994 | return NULL; | ||
995 | n=mi_alloc_gvar_chg(); | ||
996 | if (n) | ||
997 | { | ||
998 | while (r) | ||
999 | { | ||
1000 | if (r->type==t_const) | ||
1001 | { | ||
1002 | if (strcmp(r->var,"name")==0) | ||
1003 | { | ||
1004 | n->name=r->v.cstr; | ||
1005 | r->v.cstr=NULL; | ||
1006 | } | ||
1007 | else if (strcmp(r->var,"in_scope")==0) | ||
1008 | { | ||
1009 | n->in_scope=strcmp(r->v.cstr,"true")==0; | ||
1010 | } | ||
1011 | else if (strcmp(r->var,"new_type")==0) | ||
1012 | { | ||
1013 | n->new_type=r->v.cstr; | ||
1014 | r->v.cstr=NULL; | ||
1015 | } | ||
1016 | else if (strcmp(r->var,"new_num_children")==0) | ||
1017 | { | ||
1018 | n->new_num_children=atoi(r->v.cstr); | ||
1019 | } | ||
1020 | // type_changed="false" is the default | ||
1021 | } | ||
1022 | r=r->next; | ||
1023 | } | ||
1024 | } | ||
1025 | return n; | ||
1026 | } | ||
1027 | |||
1028 | int mi_res_changelist(mi_h *h, mi_gvar_chg **changed) | ||
1029 | { | ||
1030 | mi_gvar_chg *last, *n; | ||
1031 | mi_results *res=mi_res_done_var(h,"changelist"), *r; | ||
1032 | int count=0; | ||
1033 | |||
1034 | *changed=NULL; | ||
1035 | if (!res) | ||
1036 | return 0; | ||
1037 | last=NULL; | ||
1038 | count=1; | ||
1039 | n=NULL; | ||
1040 | r=res->v.rs; | ||
1041 | |||
1042 | if (res->type==t_list) | ||
1043 | {// MI v2 a list of tuples | ||
1044 | while (r) | ||
1045 | { | ||
1046 | if (r->type==t_tuple) | ||
1047 | { | ||
1048 | n=mi_get_gvar_chg(r->v.rs); | ||
1049 | if (n) | ||
1050 | { | ||
1051 | if (last) | ||
1052 | last->next=n; | ||
1053 | else | ||
1054 | *changed=n; | ||
1055 | last=n; | ||
1056 | count++; | ||
1057 | } | ||
1058 | } | ||
1059 | r=r->next; | ||
1060 | } | ||
1061 | } | ||
1062 | else if (res->type==t_tuple) | ||
1063 | {// MI v1 a tuple with all together *8-P | ||
1064 | while (r) | ||
1065 | { | ||
1066 | if (r->type==t_const) /* Just in case. */ | ||
1067 | {/* Get one var. */ | ||
1068 | if (strcmp(r->var,"name")==0) | ||
1069 | { | ||
1070 | if (n) | ||
1071 | {/* Add to the list*/ | ||
1072 | if (last) | ||
1073 | last->next=n; | ||
1074 | else | ||
1075 | *changed=n; | ||
1076 | last=n; | ||
1077 | count++; | ||
1078 | } | ||
1079 | n=mi_alloc_gvar_chg(); | ||
1080 | if (!n) | ||
1081 | { | ||
1082 | mi_free_gvar_chg(*changed); | ||
1083 | return 0; | ||
1084 | } | ||
1085 | n->name=r->v.cstr; | ||
1086 | r->v.cstr=NULL; | ||
1087 | } | ||
1088 | else if ((NULL != n) && (strcmp(r->var,"in_scope")==0)) | ||
1089 | { | ||
1090 | n->in_scope=strcmp(r->v.cstr,"true")==0; | ||
1091 | } | ||
1092 | else if ((NULL != n) && (strcmp(r->var,"new_type")==0)) | ||
1093 | { | ||
1094 | n->new_type=r->v.cstr; | ||
1095 | r->v.cstr=NULL; | ||
1096 | } | ||
1097 | else if ((NULL != n) && (strcmp(r->var,"new_num_children")==0)) | ||
1098 | { | ||
1099 | n->new_num_children=atoi(r->v.cstr); | ||
1100 | } | ||
1101 | // type_changed="false" is the default | ||
1102 | } | ||
1103 | r=r->next; | ||
1104 | } | ||
1105 | if (n) | ||
1106 | {/* Add to the list*/ | ||
1107 | if (last) | ||
1108 | last->next=n; | ||
1109 | else | ||
1110 | *changed=n; | ||
1111 | last=n; | ||
1112 | count++; | ||
1113 | } | ||
1114 | } | ||
1115 | mi_free_results(res); | ||
1116 | |||
1117 | return count; | ||
1118 | } | ||
1119 | |||
1120 | int mi_get_children(mi_results *ch, mi_gvar *v) | ||
1121 | { | ||
1122 | mi_gvar *cur=NULL, *aux; | ||
1123 | int i=0, count=v->numchild, l; | ||
1124 | |||
1125 | while (ch) | ||
1126 | { | ||
1127 | if (strcmp(ch->var,"child")==0 && ch->type==t_tuple && i<count) | ||
1128 | { | ||
1129 | mi_results *r=ch->v.rs; | ||
1130 | aux=mi_alloc_gvar(); | ||
1131 | if (!aux) | ||
1132 | return 0; | ||
1133 | if (!v->child) | ||
1134 | v->child=aux; | ||
1135 | else if (NULL != cur) | ||
1136 | cur->next=aux; | ||
1137 | cur=aux; | ||
1138 | cur->parent=v; | ||
1139 | cur->depth=v->depth+1; | ||
1140 | |||
1141 | while (r) | ||
1142 | { | ||
1143 | if (r->type==t_const) | ||
1144 | { | ||
1145 | if (strcmp(r->var,"name")==0) | ||
1146 | { | ||
1147 | cur->name=r->v.cstr; | ||
1148 | r->v.cstr=NULL; | ||
1149 | } | ||
1150 | else if (strcmp(r->var,"exp")==0) | ||
1151 | { | ||
1152 | cur->exp=r->v.cstr; | ||
1153 | r->v.cstr=NULL; | ||
1154 | } | ||
1155 | else if (strcmp(r->var,"type")==0) | ||
1156 | { | ||
1157 | cur->type=r->v.cstr; | ||
1158 | r->v.cstr=NULL; | ||
1159 | l=strlen(cur->type); | ||
1160 | if (l && cur->type[l-1]=='*') | ||
1161 | cur->ispointer=1; | ||
1162 | } | ||
1163 | else if (strcmp(r->var,"value")==0) | ||
1164 | { | ||
1165 | cur->value=r->v.cstr; | ||
1166 | r->v.cstr=NULL; | ||
1167 | } | ||
1168 | else if (strcmp(r->var,"numchild")==0) | ||
1169 | { | ||
1170 | cur->numchild=atoi(r->v.cstr); | ||
1171 | } | ||
1172 | } | ||
1173 | r=r->next; | ||
1174 | } | ||
1175 | i++; | ||
1176 | } | ||
1177 | ch=ch->next; | ||
1178 | } | ||
1179 | v->vischild=i; | ||
1180 | v->opened=1; | ||
1181 | return i==v->numchild; | ||
1182 | } | ||
1183 | |||
1184 | int mi_res_children(mi_h *h, mi_gvar *v) | ||
1185 | { | ||
1186 | mi_output *r, *res; | ||
1187 | int ok=0; | ||
1188 | |||
1189 | r=mi_get_response_blk(h); | ||
1190 | res=mi_get_rrecord(r); | ||
1191 | if (res && res->tclass==MI_CL_DONE) | ||
1192 | { | ||
1193 | mi_results *num=mi_get_var(res,"numchild"); | ||
1194 | if (num && num->type==t_const) | ||
1195 | { | ||
1196 | v->numchild=atoi(num->v.cstr); | ||
1197 | if (v->child) | ||
1198 | { | ||
1199 | mi_free_gvar(v->child); | ||
1200 | v->child=NULL; | ||
1201 | } | ||
1202 | if (v->numchild) | ||
1203 | { | ||
1204 | mi_results *ch =mi_get_var(res,"children"); | ||
1205 | if (ch && ch->type!=t_const) /* MI v1 tuple, MI v2 list */ | ||
1206 | ok=mi_get_children(ch->v.rs,v); | ||
1207 | } | ||
1208 | else | ||
1209 | ok=1; | ||
1210 | } | ||
1211 | } | ||
1212 | mi_free_output(r); | ||
1213 | return ok; | ||
1214 | } | ||
1215 | |||
1216 | mi_bkpt *mi_get_bkpt(mi_results *p) | ||
1217 | { | ||
1218 | mi_bkpt *res; | ||
1219 | char *end; | ||
1220 | |||
1221 | res=mi_alloc_bkpt(); | ||
1222 | if (!res) | ||
1223 | return NULL; | ||
1224 | while (p) | ||
1225 | { | ||
1226 | if (p->type==t_const && p->var) | ||
1227 | { | ||
1228 | if (strcmp(p->var,"number")==0) | ||
1229 | res->number=atoi(p->v.cstr); | ||
1230 | else if (strcmp(p->var,"type")==0) | ||
1231 | { | ||
1232 | if (strcmp(p->v.cstr,"breakpoint")==0) | ||
1233 | res->type=t_breakpoint; | ||
1234 | else | ||
1235 | res->type=t_unknown; | ||
1236 | } | ||
1237 | else if (strcmp(p->var,"disp")==0) | ||
1238 | { | ||
1239 | if (strcmp(p->v.cstr,"keep")==0) | ||
1240 | res->disp=d_keep; | ||
1241 | else if (strcmp(p->v.cstr,"del")==0) | ||
1242 | res->disp=d_del; | ||
1243 | else | ||
1244 | res->disp=d_unknown; | ||
1245 | } | ||
1246 | else if (strcmp(p->var,"enabled")==0) | ||
1247 | res->enabled=p->v.cstr[0]=='y'; | ||
1248 | else if (strcmp(p->var,"addr")==0) | ||
1249 | res->addr=(void *)strtoul(p->v.cstr,&end,0); | ||
1250 | else if (strcmp(p->var,"func")==0) | ||
1251 | { | ||
1252 | res->func=p->v.cstr; | ||
1253 | p->v.cstr=NULL; | ||
1254 | } | ||
1255 | else if (strcmp(p->var,"file")==0) | ||
1256 | { | ||
1257 | res->file=p->v.cstr; | ||
1258 | p->v.cstr=NULL; | ||
1259 | } | ||
1260 | else if (strcmp(p->var,"line")==0) | ||
1261 | res->line=atoi(p->v.cstr); | ||
1262 | else if (strcmp(p->var,"times")==0) | ||
1263 | res->times=atoi(p->v.cstr); | ||
1264 | else if (strcmp(p->var,"ignore")==0) | ||
1265 | res->ignore=atoi(p->v.cstr); | ||
1266 | else if (strcmp(p->var,"cond")==0) | ||
1267 | { | ||
1268 | res->cond=p->v.cstr; | ||
1269 | p->v.cstr=NULL; | ||
1270 | } | ||
1271 | } | ||
1272 | p=p->next; | ||
1273 | } | ||
1274 | return res; | ||
1275 | } | ||
1276 | |||
1277 | mi_bkpt *mi_res_bkpt(mi_h *h) | ||
1278 | { | ||
1279 | mi_results *r=mi_res_done_var(h,"bkpt"); | ||
1280 | mi_bkpt *b=NULL; | ||
1281 | |||
1282 | if (r && r->type==t_tuple) | ||
1283 | b=mi_get_bkpt(r->v.rs); | ||
1284 | mi_free_results(r); | ||
1285 | return b; | ||
1286 | } | ||
1287 | |||
1288 | mi_wp *mi_get_wp(mi_results *p, enum mi_wp_mode m) | ||
1289 | { | ||
1290 | mi_wp *res=mi_alloc_wp(); | ||
1291 | |||
1292 | if (res) | ||
1293 | { | ||
1294 | res->mode=m; | ||
1295 | while (p) | ||
1296 | { | ||
1297 | if (p->type==t_const && p->var) | ||
1298 | { | ||
1299 | if (strcmp(p->var,"number")==0) | ||
1300 | { | ||
1301 | res->number=atoi(p->v.cstr); | ||
1302 | res->enabled=1; | ||
1303 | } | ||
1304 | else if (strcmp(p->var,"exp")==0) | ||
1305 | { | ||
1306 | res->exp=p->v.cstr; | ||
1307 | p->v.cstr=NULL; | ||
1308 | } | ||
1309 | } | ||
1310 | p=p->next; | ||
1311 | } | ||
1312 | } | ||
1313 | return res; | ||
1314 | } | ||
1315 | |||
1316 | mi_wp *mi_parse_wp_res(mi_output *r) | ||
1317 | { | ||
1318 | mi_results *p; | ||
1319 | enum mi_wp_mode m=wm_unknown; | ||
1320 | |||
1321 | /* The info is in a result wpt=... */ | ||
1322 | p=r->c; | ||
1323 | while (p) | ||
1324 | { | ||
1325 | if (p->var) | ||
1326 | { | ||
1327 | if (strcmp(p->var,"wpt")==0) | ||
1328 | m=wm_write; | ||
1329 | else if (strcmp(p->var,"hw-rwpt")==0) | ||
1330 | m=wm_read; | ||
1331 | else if (strcmp(p->var,"hw-awpt")==0) | ||
1332 | m=wm_rw; | ||
1333 | if (m!=wm_unknown) | ||
1334 | break; | ||
1335 | } | ||
1336 | p=p->next; | ||
1337 | } | ||
1338 | if (!p || p->type!=t_tuple) | ||
1339 | return NULL; | ||
1340 | /* Scan the values inside it. */ | ||
1341 | return mi_get_wp(p->v.rs,m); | ||
1342 | } | ||
1343 | |||
1344 | mi_wp *mi_res_wp(mi_h *h) | ||
1345 | { | ||
1346 | mi_output *r, *res; | ||
1347 | mi_wp *ret=NULL; | ||
1348 | |||
1349 | r=mi_get_response_blk(h); | ||
1350 | res=mi_get_rrecord(r); | ||
1351 | |||
1352 | if (res) | ||
1353 | ret=mi_parse_wp_res(res); | ||
1354 | |||
1355 | mi_free_output(r); | ||
1356 | return ret; | ||
1357 | } | ||
1358 | |||
1359 | char *mi_res_value(mi_h *h) | ||
1360 | { | ||
1361 | mi_results *r=mi_res_done_var(h,"value"); | ||
1362 | char *s=NULL; | ||
1363 | |||
1364 | if (r && r->type==t_const) | ||
1365 | { | ||
1366 | s=r->v.cstr; | ||
1367 | r->v.rs=NULL; | ||
1368 | } | ||
1369 | mi_free_results(r); | ||
1370 | return s; | ||
1371 | } | ||
1372 | |||
1373 | mi_output *mi_get_stop_record(mi_output *r) | ||
1374 | { | ||
1375 | while (r) | ||
1376 | { | ||
1377 | if (r->type==MI_T_OUT_OF_BAND && r->stype==MI_ST_ASYNC && | ||
1378 | r->sstype==MI_SST_EXEC && r->tclass==MI_CL_STOPPED) | ||
1379 | return r; | ||
1380 | r=r->next; | ||
1381 | } | ||
1382 | return r; | ||
1383 | } | ||
1384 | |||
1385 | static | ||
1386 | char *reason_names[]= | ||
1387 | { | ||
1388 | "breakpoint-hit", | ||
1389 | "watchpoint-trigger", | ||
1390 | "read-watchpoint-trigger", | ||
1391 | "access-watchpoint-trigger", | ||
1392 | "watchpoint-scope", | ||
1393 | "function-finished", | ||
1394 | "location-reached", | ||
1395 | "end-stepping-range", | ||
1396 | "exited-signalled", | ||
1397 | "exited", | ||
1398 | "exited-normally", | ||
1399 | "signal-received" | ||
1400 | }; | ||
1401 | |||
1402 | static | ||
1403 | enum mi_stop_reason reason_values[]= | ||
1404 | { | ||
1405 | sr_bkpt_hit, | ||
1406 | sr_wp_trigger, sr_read_wp_trigger, sr_access_wp_trigger, sr_wp_scope, | ||
1407 | sr_function_finished, sr_location_reached, sr_end_stepping_range, | ||
1408 | sr_exited_signalled, sr_exited, sr_exited_normally, | ||
1409 | sr_signal_received | ||
1410 | }; | ||
1411 | |||
1412 | static | ||
1413 | char *reason_expl[]= | ||
1414 | { | ||
1415 | "Hit a breakpoint", | ||
1416 | "Write watchpoint", | ||
1417 | "Read watchpoint", | ||
1418 | "Access watchpoint", | ||
1419 | "Watchpoint out of scope", | ||
1420 | "Function finished", | ||
1421 | "Location reached", | ||
1422 | "End of stepping", | ||
1423 | "Exited signalled", | ||
1424 | "Exited with error", | ||
1425 | "Exited normally", | ||
1426 | "Signal received" | ||
1427 | }; | ||
1428 | |||
1429 | enum mi_stop_reason mi_reason_str_to_enum(const char *s) | ||
1430 | { | ||
1431 | int i; | ||
1432 | |||
1433 | for (i=0; i<sizeof(reason_names)/sizeof(char *); i++) | ||
1434 | if (strcmp(reason_names[i],s)==0) | ||
1435 | return reason_values[i]; | ||
1436 | return sr_unknown; | ||
1437 | } | ||
1438 | |||
1439 | const char *mi_reason_enum_to_str(enum mi_stop_reason r) | ||
1440 | { | ||
1441 | int i; | ||
1442 | |||
1443 | if (r==sr_unknown) | ||
1444 | return "Unknown (temp bkp?)"; | ||
1445 | for (i=0; i<sizeof(reason_values)/sizeof(char *); i++) | ||
1446 | if (reason_values[i]==r) | ||
1447 | return reason_expl[i]; | ||
1448 | return NULL; | ||
1449 | } | ||
1450 | |||
1451 | mi_stop *mi_get_stopped(mi_results *r) | ||
1452 | { | ||
1453 | mi_stop *res=mi_alloc_stop(); | ||
1454 | |||
1455 | if (res) | ||
1456 | { | ||
1457 | while (r) | ||
1458 | { | ||
1459 | if (r->type==t_const) | ||
1460 | { | ||
1461 | if (strcmp(r->var,"reason")==0) | ||
1462 | res->reason=mi_reason_str_to_enum(r->v.cstr); | ||
1463 | else if (!res->have_thread_id && strcmp(r->var,"thread-id")==0) | ||
1464 | { | ||
1465 | res->have_thread_id=1; | ||
1466 | res->thread_id=atoi(r->v.cstr); | ||
1467 | } | ||
1468 | else if (!res->have_bkptno && strcmp(r->var,"bkptno")==0) | ||
1469 | { | ||
1470 | res->have_bkptno=1; | ||
1471 | res->bkptno=atoi(r->v.cstr); | ||
1472 | } | ||
1473 | else if (!res->have_bkptno && strcmp(r->var,"wpnum")==0) | ||
1474 | { | ||
1475 | res->have_wpno=1; | ||
1476 | res->wpno=atoi(r->v.cstr); | ||
1477 | } | ||
1478 | else if (strcmp(r->var,"gdb-result-var")==0) | ||
1479 | { | ||
1480 | res->gdb_result_var=r->v.cstr; | ||
1481 | r->v.cstr=NULL; | ||
1482 | } | ||
1483 | else if (strcmp(r->var,"return-value")==0) | ||
1484 | { | ||
1485 | res->return_value=r->v.cstr; | ||
1486 | r->v.cstr=NULL; | ||
1487 | } | ||
1488 | else if (strcmp(r->var,"signal-name")==0) | ||
1489 | { | ||
1490 | res->signal_name=r->v.cstr; | ||
1491 | r->v.cstr=NULL; | ||
1492 | } | ||
1493 | else if (strcmp(r->var,"signal-meaning")==0) | ||
1494 | { | ||
1495 | res->signal_meaning=r->v.cstr; | ||
1496 | r->v.cstr=NULL; | ||
1497 | } | ||
1498 | else if (!res->have_exit_code && strcmp(r->var,"exit-code")==0) | ||
1499 | { | ||
1500 | res->have_exit_code=1; | ||
1501 | res->exit_code=atoi(r->v.cstr); | ||
1502 | } | ||
1503 | } | ||
1504 | else // tuple or list | ||
1505 | { | ||
1506 | if (strcmp(r->var,"frame")==0) | ||
1507 | res->frame=mi_parse_frame(r->v.rs); | ||
1508 | else if (!res->wp && strcmp(r->var,"wpt")==0) | ||
1509 | res->wp=mi_get_wp(r->v.rs,wm_write); | ||
1510 | else if (!res->wp && strcmp(r->var,"hw-rwpt")==0) | ||
1511 | res->wp=mi_get_wp(r->v.rs,wm_read); | ||
1512 | else if (!res->wp && strcmp(r->var,"hw-awpt")==0) | ||
1513 | res->wp=mi_get_wp(r->v.rs,wm_rw); | ||
1514 | else if (!(res->wp_old || res->wp_val) && strcmp(r->var,"value")==0) | ||
1515 | { | ||
1516 | mi_results *p=r->v.rs; | ||
1517 | while (p) | ||
1518 | { | ||
1519 | if (strcmp(p->var,"value")==0 || strcmp(p->var,"new")==0) | ||
1520 | { | ||
1521 | res->wp_val=p->v.cstr; | ||
1522 | p->v.cstr=NULL; | ||
1523 | } | ||
1524 | else if (strcmp(p->var,"old")==0) | ||
1525 | { | ||
1526 | res->wp_old=p->v.cstr; | ||
1527 | p->v.cstr=NULL; | ||
1528 | } | ||
1529 | p=p->next; | ||
1530 | } | ||
1531 | } | ||
1532 | } | ||
1533 | r=r->next; | ||
1534 | } | ||
1535 | } | ||
1536 | return res; | ||
1537 | } | ||
1538 | |||
1539 | mi_stop *mi_res_stop(mi_h *h) | ||
1540 | { | ||
1541 | mi_output *o=mi_retire_response(h); | ||
1542 | mi_stop *stop=NULL; | ||
1543 | |||
1544 | if (o) | ||
1545 | { | ||
1546 | mi_output *sr=mi_get_stop_record(o); | ||
1547 | if (sr) | ||
1548 | stop=mi_get_stopped(sr->c); | ||
1549 | } | ||
1550 | mi_free_output(o); | ||
1551 | |||
1552 | return stop; | ||
1553 | } | ||
1554 | |||
1555 | int mi_get_read_memory(mi_h *h, unsigned char *dest, unsigned ws, int *na, | ||
1556 | unsigned long *addr) | ||
1557 | { | ||
1558 | char *end; | ||
1559 | mi_results *res=mi_res_done_var(h,"memory"), *r; | ||
1560 | int ok=0; | ||
1561 | |||
1562 | *na=0; | ||
1563 | r=res; | ||
1564 | if (r && r->type==t_list && ws==1) | ||
1565 | { | ||
1566 | r=r->v.rs; | ||
1567 | if (r->type!=t_tuple) | ||
1568 | { | ||
1569 | mi_free_results(res); | ||
1570 | return 0; | ||
1571 | } | ||
1572 | r=r->v.rs; | ||
1573 | while (r) | ||
1574 | { | ||
1575 | if (r->type==t_list && strcmp(r->var,"data")==0) | ||
1576 | { | ||
1577 | mi_results *data=r->v.rs; | ||
1578 | ok++; | ||
1579 | if (data && data->type==t_const && | ||
1580 | strcmp(data->v.cstr,"N/A")==0) | ||
1581 | *na=1; | ||
1582 | else | ||
1583 | while (data) | ||
1584 | { | ||
1585 | if (data->type==t_const) | ||
1586 | *(dest++)=strtol(data->v.cstr,&end,0); | ||
1587 | data=data->next; | ||
1588 | } | ||
1589 | } | ||
1590 | else if (r->type==t_const && strcmp(r->var,"addr")==0) | ||
1591 | { | ||
1592 | ok++; | ||
1593 | if (addr) | ||
1594 | *addr=strtoul(r->v.cstr,&end,0); | ||
1595 | } | ||
1596 | r=r->next; | ||
1597 | } | ||
1598 | |||
1599 | } | ||
1600 | mi_free_results(res); | ||
1601 | return ok==2; | ||
1602 | } | ||
1603 | |||
1604 | mi_asm_insn *mi_parse_insn(mi_results *c) | ||
1605 | { | ||
1606 | mi_asm_insn *res=NULL, *cur=NULL; | ||
1607 | mi_results *sub; | ||
1608 | char *end; | ||
1609 | |||
1610 | while (c) | ||
1611 | { | ||
1612 | if (c->type==t_tuple) | ||
1613 | { | ||
1614 | if (!res) | ||
1615 | res=cur=mi_alloc_asm_insn(); | ||
1616 | else | ||
1617 | { | ||
1618 | cur->next=mi_alloc_asm_insn(); | ||
1619 | cur=cur->next; | ||
1620 | } | ||
1621 | if (!cur) | ||
1622 | { | ||
1623 | mi_free_asm_insn(res); | ||
1624 | return NULL; | ||
1625 | } | ||
1626 | sub=c->v.rs; | ||
1627 | while (sub) | ||
1628 | { | ||
1629 | if (sub->type==t_const) | ||
1630 | { | ||
1631 | if (strcmp(sub->var,"address")==0) | ||
1632 | cur->addr=(void *)strtoul(sub->v.cstr,&end,0); | ||
1633 | else if (strcmp(sub->var,"func-name")==0) | ||
1634 | { | ||
1635 | cur->func=sub->v.cstr; | ||
1636 | sub->v.cstr=NULL; | ||
1637 | } | ||
1638 | else if (strcmp(sub->var,"offset")==0) | ||
1639 | cur->offset=atoi(sub->v.cstr); | ||
1640 | else if (strcmp(sub->var,"inst")==0) | ||
1641 | { | ||
1642 | cur->inst=sub->v.cstr; | ||
1643 | sub->v.cstr=NULL; | ||
1644 | } | ||
1645 | } | ||
1646 | sub=sub->next; | ||
1647 | } | ||
1648 | } | ||
1649 | c=c->next; | ||
1650 | } | ||
1651 | return res; | ||
1652 | } | ||
1653 | |||
1654 | mi_asm_insns *mi_parse_insns(mi_results *c) | ||
1655 | { | ||
1656 | mi_asm_insns *res=NULL, *cur=NULL; | ||
1657 | mi_results *sub; | ||
1658 | |||
1659 | while (c) | ||
1660 | { | ||
1661 | if (c->var) | ||
1662 | { | ||
1663 | if (strcmp(c->var,"src_and_asm_line")==0 && c->type==t_tuple) | ||
1664 | { | ||
1665 | if (!res) | ||
1666 | res=cur=mi_alloc_asm_insns(); | ||
1667 | else | ||
1668 | { | ||
1669 | cur->next=mi_alloc_asm_insns(); | ||
1670 | cur=cur->next; | ||
1671 | } | ||
1672 | if (!cur) | ||
1673 | { | ||
1674 | mi_free_asm_insns(res); | ||
1675 | return NULL; | ||
1676 | } | ||
1677 | sub=c->v.rs; | ||
1678 | while (sub) | ||
1679 | { | ||
1680 | if (sub->var) | ||
1681 | { | ||
1682 | if (sub->type==t_const) | ||
1683 | { | ||
1684 | if (strcmp(sub->var,"line")==0) | ||
1685 | cur->line=atoi(sub->v.cstr); | ||
1686 | else if (strcmp(sub->var,"file")==0) | ||
1687 | { | ||
1688 | cur->file=sub->v.cstr; | ||
1689 | sub->v.cstr=NULL; | ||
1690 | } | ||
1691 | } | ||
1692 | else if (sub->type==t_list) | ||
1693 | { | ||
1694 | if (strcmp(sub->var,"line_asm_insn")==0) | ||
1695 | cur->ins=mi_parse_insn(sub->v.rs); | ||
1696 | } | ||
1697 | } | ||
1698 | sub=sub->next; | ||
1699 | } | ||
1700 | } | ||
1701 | } | ||
1702 | else | ||
1703 | {/* No source line, just instructions */ | ||
1704 | res=mi_alloc_asm_insns(); | ||
1705 | res->ins=mi_parse_insn(c); | ||
1706 | break; | ||
1707 | } | ||
1708 | c=c->next; | ||
1709 | } | ||
1710 | return res; | ||
1711 | } | ||
1712 | |||
1713 | |||
1714 | mi_asm_insns *mi_get_asm_insns(mi_h *h) | ||
1715 | { | ||
1716 | mi_results *r=mi_res_done_var(h,"asm_insns"); | ||
1717 | mi_asm_insns *f=NULL; | ||
1718 | |||
1719 | if (r && r->type==t_list) | ||
1720 | f=mi_parse_insns(r->v.rs); | ||
1721 | mi_free_results(r); | ||
1722 | return f; | ||
1723 | } | ||
1724 | |||
1725 | mi_chg_reg *mi_parse_list_regs(mi_results *r, int *how_many) | ||
1726 | { | ||
1727 | mi_results *c=r; | ||
1728 | int cregs=0; | ||
1729 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1730 | |||
1731 | /* Create the list. */ | ||
1732 | while (c) | ||
1733 | { | ||
1734 | if (c->type==t_const && !c->var) | ||
1735 | { | ||
1736 | if (first) | ||
1737 | cur=cur->next=mi_alloc_chg_reg(); | ||
1738 | else | ||
1739 | first=cur=mi_alloc_chg_reg(); | ||
1740 | |||
1741 | if (NULL != cur) { | ||
1742 | cur->name=c->v.cstr; | ||
1743 | cur->reg=cregs++; | ||
1744 | c->v.cstr=NULL; | ||
1745 | } | ||
1746 | } | ||
1747 | c=c->next; | ||
1748 | } | ||
1749 | if (how_many) | ||
1750 | *how_many=cregs; | ||
1751 | |||
1752 | return first; | ||
1753 | } | ||
1754 | |||
1755 | mi_chg_reg *mi_get_list_registers(mi_h *h, int *how_many) | ||
1756 | { | ||
1757 | mi_results *r=mi_res_done_var(h,"register-names"); | ||
1758 | mi_chg_reg *l=NULL; | ||
1759 | |||
1760 | if (r && r->type==t_list) | ||
1761 | l=mi_parse_list_regs(r->v.rs,how_many); | ||
1762 | mi_free_results(r); | ||
1763 | return l; | ||
1764 | } | ||
1765 | |||
1766 | mi_chg_reg *mi_parse_list_changed_regs(mi_results *r) | ||
1767 | { | ||
1768 | mi_results *c=r; | ||
1769 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1770 | |||
1771 | /* Create the list. */ | ||
1772 | while (c) | ||
1773 | { | ||
1774 | if (c->type==t_const && !c->var) | ||
1775 | { | ||
1776 | if (first) | ||
1777 | cur=cur->next=mi_alloc_chg_reg(); | ||
1778 | else | ||
1779 | first=cur=mi_alloc_chg_reg(); | ||
1780 | cur->reg=atoi(c->v.cstr); | ||
1781 | } | ||
1782 | c=c->next; | ||
1783 | } | ||
1784 | |||
1785 | return first; | ||
1786 | } | ||
1787 | |||
1788 | mi_chg_reg *mi_get_list_changed_regs(mi_h *h) | ||
1789 | { | ||
1790 | mi_results *r=mi_res_done_var(h,"changed-registers"); | ||
1791 | mi_chg_reg *changed=NULL; | ||
1792 | |||
1793 | if (r && r->type==t_list) | ||
1794 | changed=mi_parse_list_changed_regs(r->v.rs); | ||
1795 | mi_free_results(r); | ||
1796 | return changed; | ||
1797 | } | ||
1798 | |||
1799 | int mi_parse_reg_values(mi_results *r, mi_chg_reg *l) | ||
1800 | { | ||
1801 | mi_results *c; | ||
1802 | |||
1803 | while (r && l) | ||
1804 | { | ||
1805 | if (r->type==t_tuple && !r->var) | ||
1806 | { | ||
1807 | c=r->v.rs; | ||
1808 | while (c) | ||
1809 | { | ||
1810 | if (c->type==t_const && c->var) | ||
1811 | { | ||
1812 | if (strcmp(c->var,"number")==0) | ||
1813 | { | ||
1814 | if (atoi(c->v.cstr)!=l->reg) | ||
1815 | { | ||
1816 | mi_error=MI_PARSER; | ||
1817 | return 0; | ||
1818 | } | ||
1819 | } | ||
1820 | else if (strcmp(c->var,"value")==0) | ||
1821 | { | ||
1822 | l->val=c->v.cstr; | ||
1823 | c->v.cstr=NULL; | ||
1824 | } | ||
1825 | } | ||
1826 | c=c->next; | ||
1827 | } | ||
1828 | } | ||
1829 | r=r->next; | ||
1830 | l=l->next; | ||
1831 | } | ||
1832 | |||
1833 | return !l && !r; | ||
1834 | } | ||
1835 | |||
1836 | int mi_get_reg_values(mi_h *h, mi_chg_reg *l) | ||
1837 | { | ||
1838 | mi_results *r=mi_res_done_var(h,"register-values"); | ||
1839 | int ok=0; | ||
1840 | |||
1841 | if (r && r->type==t_list) | ||
1842 | ok=mi_parse_reg_values(r->v.rs,l); | ||
1843 | mi_free_results(r); | ||
1844 | return ok; | ||
1845 | } | ||
1846 | |||
1847 | int mi_parse_list_regs_l(mi_results *r, mi_chg_reg *l) | ||
1848 | { | ||
1849 | while (r && l) | ||
1850 | { | ||
1851 | if (r->type==t_const && !r->var) | ||
1852 | { | ||
1853 | free(l->name); | ||
1854 | l->name=r->v.cstr; | ||
1855 | r->v.cstr=NULL; | ||
1856 | l=l->next; | ||
1857 | } | ||
1858 | r=r->next; | ||
1859 | } | ||
1860 | |||
1861 | return !l && !r; | ||
1862 | } | ||
1863 | |||
1864 | int mi_get_list_registers_l(mi_h *h, mi_chg_reg *l) | ||
1865 | { | ||
1866 | mi_results *r=mi_res_done_var(h,"register-names"); | ||
1867 | int ok=0; | ||
1868 | |||
1869 | if (r && r->type==t_list) | ||
1870 | ok=mi_parse_list_regs_l(r->v.rs,l); | ||
1871 | mi_free_results(r); | ||
1872 | return ok; | ||
1873 | } | ||
1874 | |||
1875 | mi_chg_reg *mi_parse_reg_values_l(mi_results *r, int *how_many) | ||
1876 | { | ||
1877 | mi_results *c; | ||
1878 | mi_chg_reg *first=NULL, *cur=NULL; | ||
1879 | *how_many=0; | ||
1880 | |||
1881 | while (r) | ||
1882 | { | ||
1883 | if (r->type==t_tuple && !r->var) | ||
1884 | { | ||
1885 | c=r->v.rs; | ||
1886 | if (first) | ||
1887 | cur=cur->next=mi_alloc_chg_reg(); | ||
1888 | else | ||
1889 | first=cur=mi_alloc_chg_reg(); | ||
1890 | while (c) | ||
1891 | { | ||
1892 | if (c->type==t_const && c->var) | ||
1893 | { | ||
1894 | if (strcmp(c->var,"number")==0) | ||
1895 | { | ||
1896 | if (NULL != cur) | ||
1897 | cur->reg=atoi(c->v.cstr); | ||
1898 | (*how_many)++; | ||
1899 | } | ||
1900 | else if (strcmp(c->var,"value")==0) | ||
1901 | { | ||
1902 | if (NULL != cur) | ||
1903 | cur->val=c->v.cstr; | ||
1904 | c->v.cstr=NULL; | ||
1905 | } | ||
1906 | } | ||
1907 | c=c->next; | ||
1908 | } | ||
1909 | } | ||
1910 | r=r->next; | ||
1911 | } | ||
1912 | |||
1913 | return first; | ||
1914 | } | ||
1915 | |||
1916 | mi_chg_reg *mi_get_reg_values_l(mi_h *h, int *how_many) | ||
1917 | { | ||
1918 | mi_results *r=mi_res_done_var(h,"register-values"); | ||
1919 | mi_chg_reg *rgs=NULL; | ||
1920 | |||
1921 | if (r && r->type==t_list) | ||
1922 | rgs=mi_parse_reg_values_l(r->v.rs,how_many); | ||
1923 | mi_free_results(r); | ||
1924 | return rgs; | ||
1925 | } | ||
1926 | |||
1927 | |||
diff --git a/pathologist/src/mi/gdbmi_prg_control.c b/pathologist/src/mi/gdbmi_prg_control.c new file mode 100644 index 0000000..80ac7af --- /dev/null +++ b/pathologist/src/mi/gdbmi_prg_control.c | |||
@@ -0,0 +1,598 @@ | |||
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 "gdbmi.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_set_unwind_on_signal_on(mi_h *h) | ||
76 | { | ||
77 | mi_send(h, "set unwindonsignal on\n"); | ||
78 | } | ||
79 | |||
80 | void mi_exec_arguments(mi_h *h, const char *args) | ||
81 | { | ||
82 | mi_send(h,"-exec-arguments %s\n",args); | ||
83 | } | ||
84 | |||
85 | void mi_exec_run(mi_h *h) | ||
86 | { | ||
87 | mi_send(h,"-exec-run\n"); | ||
88 | } | ||
89 | |||
90 | void mi_exec_continue(mi_h *h) | ||
91 | { | ||
92 | mi_send(h,"-exec-continue\n"); | ||
93 | } | ||
94 | |||
95 | void mi_exec_continue_reverse(mi_h *h) | ||
96 | { | ||
97 | mi_send(h, "-exec-continue --reverse\n"); | ||
98 | } | ||
99 | |||
100 | void mi_target_terminal(mi_h *h, const char *tty_name) | ||
101 | { | ||
102 | mi_send(h,"tty %s\n",tty_name); | ||
103 | } | ||
104 | |||
105 | void mi_file_symbol_file(mi_h *h, const char *file) | ||
106 | { | ||
107 | if (mi_get_workaround(MI_PSYM_SEARCH)) | ||
108 | mi_send(h,"symbol-file %s -readnow\n",file); | ||
109 | else | ||
110 | mi_send(h,"-file-symbol-file %s\n",file); | ||
111 | } | ||
112 | |||
113 | void mi_exec_finish(mi_h *h) | ||
114 | { | ||
115 | mi_send(h,"-exec-finish\n"); | ||
116 | } | ||
117 | |||
118 | void mi_exec_finish_reverse(mi_h * h) | ||
119 | { | ||
120 | mi_send(h, "-exec-finish --reverse\n"); | ||
121 | } | ||
122 | void mi_exec_interrupt(mi_h *h) | ||
123 | { | ||
124 | mi_send(h,"-exec-interrupt\n"); | ||
125 | } | ||
126 | |||
127 | void mi_exec_record_process(mi_h *h) | ||
128 | { | ||
129 | mi_send(h, "-interpreter-exec mi record\n"); | ||
130 | } | ||
131 | |||
132 | void mi_exec_record_stop(mi_h *h) | ||
133 | { | ||
134 | mi_send(h, "-interpreter-exec mi record stop\n"); | ||
135 | } | ||
136 | |||
137 | void mi_exec_next(mi_h *h, int count) | ||
138 | { | ||
139 | if (count>1) | ||
140 | mi_send(h,"-exec-next %d\n",count); | ||
141 | else | ||
142 | mi_send(h,"-exec-next\n"); | ||
143 | } | ||
144 | |||
145 | void mi_exec_next_instruction(mi_h *h) | ||
146 | { | ||
147 | mi_send(h,"-exec-next-instruction\n"); | ||
148 | } | ||
149 | |||
150 | void mi_exec_step(mi_h *h, int count) | ||
151 | { | ||
152 | if (count>1) | ||
153 | mi_send(h,"-exec-step %d\n",count); | ||
154 | else | ||
155 | mi_send(h,"-exec-step\n"); | ||
156 | } | ||
157 | |||
158 | |||
159 | void mi_exec_step_back(mi_h *h, int count) | ||
160 | { | ||
161 | if (count>1) | ||
162 | mi_send(h, "-exec-step %d --reverse\n", count); | ||
163 | else | ||
164 | mi_send(h, "-exec-step --reverse\n"); | ||
165 | } | ||
166 | |||
167 | |||
168 | void mi_exec_next_reverse(mi_h *h, int count) | ||
169 | { | ||
170 | if (count>1) | ||
171 | mi_send(h, "-exec-next %d --reverse\n", count); | ||
172 | else | ||
173 | mi_send(h, "-exec-next --reverse\n"); | ||
174 | } | ||
175 | |||
176 | |||
177 | void mi_exec_step_instruction(mi_h *h) | ||
178 | { | ||
179 | mi_send(h,"-exec-step-instruction\n"); | ||
180 | } | ||
181 | |||
182 | void mi_exec_until(mi_h *h, const char *file, int line) | ||
183 | { | ||
184 | if (!file) | ||
185 | mi_send(h,"-exec-until\n"); | ||
186 | else | ||
187 | mi_send(h,"-exec-until %s:%d\n",file,line); | ||
188 | } | ||
189 | |||
190 | void mi_exec_until_addr(mi_h *h, void *addr) | ||
191 | { | ||
192 | mi_send(h,"-exec-until *%p\n",addr); | ||
193 | } | ||
194 | |||
195 | void mi_exec_return(mi_h *h) | ||
196 | { | ||
197 | mi_send(h,"-exec-return\n"); | ||
198 | } | ||
199 | |||
200 | void mi_exec_kill(mi_h *h) | ||
201 | { | ||
202 | mi_send(h,"kill\n"); | ||
203 | } | ||
204 | |||
205 | /* High level versions. */ | ||
206 | |||
207 | /**[txh]******************************************************************** | ||
208 | |||
209 | Description: | ||
210 | Specify the executable and arguments for local debug. | ||
211 | |||
212 | Command: -file-exec-and-symbols + -exec-arguments | ||
213 | Return: !=0 OK | ||
214 | |||
215 | ***************************************************************************/ | ||
216 | |||
217 | int gmi_set_exec(mi_h *h, const char *file, const char *args) | ||
218 | { | ||
219 | mi_file_exec_and_symbols(h,file); | ||
220 | if (!mi_res_simple_done(h)) | ||
221 | return 0; | ||
222 | if (!args) | ||
223 | return 1; | ||
224 | mi_exec_arguments(h,args); | ||
225 | return mi_res_simple_done(h); | ||
226 | } | ||
227 | |||
228 | int gmi_set_unwind_on_signal_on(mi_h * h) | ||
229 | { | ||
230 | mi_set_unwind_on_signal_on(h); | ||
231 | return mi_res_simple_running(h); | ||
232 | } | ||
233 | |||
234 | /**[txh]******************************************************************** | ||
235 | |||
236 | Description: | ||
237 | Start running the executable. Remote sessions starts running. | ||
238 | |||
239 | Command: -exec-run | ||
240 | Return: !=0 OK | ||
241 | |||
242 | ***************************************************************************/ | ||
243 | |||
244 | int gmi_exec_run(mi_h *h) | ||
245 | { | ||
246 | mi_exec_run(h); | ||
247 | return mi_res_simple_running(h); | ||
248 | } | ||
249 | |||
250 | /**[txh]******************************************************************** | ||
251 | |||
252 | Description: | ||
253 | Continue the execution after a "stop". | ||
254 | |||
255 | Command: -exec-continue | ||
256 | Return: !=0 OK | ||
257 | |||
258 | ***************************************************************************/ | ||
259 | |||
260 | int gmi_exec_continue(mi_h *h) | ||
261 | { | ||
262 | mi_exec_continue(h); | ||
263 | return mi_res_simple_running(h); | ||
264 | } | ||
265 | |||
266 | /**[txh]******************************************************************** | ||
267 | |||
268 | Description: | ||
269 | Continue the execution in reverse order after a "stop". | ||
270 | |||
271 | Command: -exec-continue --reverse | ||
272 | Return: !=0 OK | ||
273 | |||
274 | ***************************************************************************/ | ||
275 | int gmi_exec_continue_reverse(mi_h *h) | ||
276 | { | ||
277 | mi_exec_continue_reverse(h); | ||
278 | return mi_res_simple_running(h); | ||
279 | } | ||
280 | |||
281 | /**[txh]******************************************************************** | ||
282 | |||
283 | Description: | ||
284 | Indicate which terminal will use the target program. For local sessions. | ||
285 | |||
286 | Command: tty | ||
287 | Return: !=0 OK | ||
288 | Example: | ||
289 | |||
290 | ***************************************************************************/ | ||
291 | |||
292 | int gmi_target_terminal(mi_h *h, const char *tty_name) | ||
293 | { | ||
294 | mi_target_terminal(h,tty_name); | ||
295 | return mi_res_simple_done(h); | ||
296 | } | ||
297 | |||
298 | /**[txh]******************************************************************** | ||
299 | |||
300 | Description: | ||
301 | Specify what's the local copy that have debug info. For remote sessions. | ||
302 | |||
303 | Command: -file-symbol-file | ||
304 | Return: !=0 OK | ||
305 | |||
306 | ***************************************************************************/ | ||
307 | |||
308 | int gmi_file_symbol_file(mi_h *h, const char *file) | ||
309 | { | ||
310 | mi_file_symbol_file(h,file); | ||
311 | return mi_res_simple_done(h); | ||
312 | } | ||
313 | |||
314 | /**[txh]******************************************************************** | ||
315 | |||
316 | Description: | ||
317 | Continue until function return, the return value is included in the async | ||
318 | response. | ||
319 | |||
320 | Command: -exec-finish | ||
321 | Return: !=0 OK. | ||
322 | |||
323 | ***************************************************************************/ | ||
324 | |||
325 | int gmi_exec_finish(mi_h *h) | ||
326 | { | ||
327 | mi_exec_finish(h); | ||
328 | return mi_res_simple_running(h); | ||
329 | } | ||
330 | |||
331 | /**[txh]******************************************************************** | ||
332 | |||
333 | Description: | ||
334 | Continue backwardly until function return, the return value is included in | ||
335 | the async response. | ||
336 | |||
337 | Command: -exec-finish --reverse | ||
338 | Return: !=0 OK. | ||
339 | |||
340 | ***************************************************************************/ | ||
341 | int gmi_exec_finish_reverse(mi_h *h) | ||
342 | { | ||
343 | mi_exec_finish_reverse(h); | ||
344 | return mi_res_simple_running(h); | ||
345 | } | ||
346 | |||
347 | /**[txh]******************************************************************** | ||
348 | |||
349 | Description: | ||
350 | Stop the program using SIGINT. The corresponding command should be | ||
351 | -exec-interrupt but not even gdb 6.1.1 can do it because the "async" mode | ||
352 | isn't really working. | ||
353 | |||
354 | Command: -exec-interrupt [replacement] | ||
355 | Return: Always 1 | ||
356 | Example: | ||
357 | |||
358 | ***************************************************************************/ | ||
359 | |||
360 | int gmi_exec_interrupt(mi_h *h) | ||
361 | { | ||
362 | // **** IMPORTANT!!! **** Not even gdb 6.1.1 can do it because the "async" | ||
363 | // mode isn't really working. | ||
364 | //mi_exec_interrupt(h); | ||
365 | //return mi_res_simple_running(h); | ||
366 | |||
367 | kill(h->pid,SIGINT); | ||
368 | return 1; // How can I know? | ||
369 | } | ||
370 | |||
371 | /**[txh]******************************************************************** | ||
372 | |||
373 | Description: | ||
374 | Next line of code. | ||
375 | |||
376 | Command: -exec-next | ||
377 | Return: !=0 OK | ||
378 | |||
379 | ***************************************************************************/ | ||
380 | |||
381 | int gmi_exec_next(mi_h *h) | ||
382 | { | ||
383 | mi_exec_next(h,1); | ||
384 | return mi_res_simple_running(h); | ||
385 | } | ||
386 | |||
387 | /**[txh]******************************************************************** | ||
388 | |||
389 | Description: | ||
390 | Start recording a process | ||
391 | |||
392 | Command: -interpreter-exec mi record | ||
393 | Return: !=0 OK | ||
394 | |||
395 | ***************************************************************************/ | ||
396 | int gmi_exec_record_process(mi_h *h) | ||
397 | { | ||
398 | mi_exec_record_process(h); | ||
399 | return 1; | ||
400 | } | ||
401 | |||
402 | /**[txh]******************************************************************** | ||
403 | |||
404 | Description: | ||
405 | Start recording a process | ||
406 | |||
407 | Command: -interpreter-exec mi record stop | ||
408 | Return: !=0 OK | ||
409 | |||
410 | ***************************************************************************/ | ||
411 | int gmi_exec_record_stop(mi_h *h) | ||
412 | { | ||
413 | mi_exec_record_stop(h); | ||
414 | return 1; | ||
415 | } | ||
416 | |||
417 | /**[txh]******************************************************************** | ||
418 | |||
419 | Description: | ||
420 | Skip count lines of code. | ||
421 | |||
422 | Command: -exec-next count | ||
423 | Return: !=0 OK | ||
424 | |||
425 | ***************************************************************************/ | ||
426 | |||
427 | int gmi_exec_next_cnt(mi_h *h, int count) | ||
428 | { | ||
429 | mi_exec_next(h,count); | ||
430 | return mi_res_simple_running(h); | ||
431 | } | ||
432 | |||
433 | /**[txh]******************************************************************** | ||
434 | |||
435 | Description: | ||
436 | Next line of assembler code. | ||
437 | |||
438 | Command: -exec-next-instruction | ||
439 | Return: !=0 OK | ||
440 | |||
441 | ***************************************************************************/ | ||
442 | |||
443 | int gmi_exec_next_instruction(mi_h *h) | ||
444 | { | ||
445 | mi_exec_next_instruction(h); | ||
446 | return mi_res_simple_running(h); | ||
447 | } | ||
448 | |||
449 | /**[txh]******************************************************************** | ||
450 | |||
451 | Description: | ||
452 | Next line of code. Get inside functions. | ||
453 | |||
454 | Command: -exec-step | ||
455 | Return: !=0 OK | ||
456 | |||
457 | ***************************************************************************/ | ||
458 | |||
459 | int gmi_exec_step(mi_h *h) | ||
460 | { | ||
461 | mi_exec_step(h,1); | ||
462 | return mi_res_simple_running(h); | ||
463 | } | ||
464 | |||
465 | |||
466 | |||
467 | /**[txh]******************************************************************** | ||
468 | |||
469 | Description: | ||
470 | Previous line of code. Get inside functions. | ||
471 | |||
472 | Command: -exec-step --reverse | ||
473 | Return: !=0 OK | ||
474 | |||
475 | ***************************************************************************/ | ||
476 | int gmi_exec_step_back(mi_h *h) | ||
477 | { | ||
478 | mi_exec_step_back(h,1); | ||
479 | return mi_res_simple_running(h); | ||
480 | } | ||
481 | |||
482 | |||
483 | /**[txh]******************************************************************** | ||
484 | |||
485 | Description: | ||
486 | Previous line of code (do not step into). | ||
487 | |||
488 | Command: -exec-next --reverse | ||
489 | Return: !=0 OK | ||
490 | |||
491 | ***************************************************************************/ | ||
492 | int gmi_exec_next_reverse(mi_h *h) | ||
493 | { | ||
494 | mi_exec_next_reverse(h,1); | ||
495 | return mi_res_simple_running(h); | ||
496 | } | ||
497 | |||
498 | |||
499 | /**[txh]******************************************************************** | ||
500 | |||
501 | Description: | ||
502 | Next count lines of code. Get inside functions. | ||
503 | |||
504 | Command: -exec-step count | ||
505 | Return: !=0 OK | ||
506 | |||
507 | ***************************************************************************/ | ||
508 | |||
509 | int gmi_exec_step_cnt(mi_h *h, int count) | ||
510 | { | ||
511 | mi_exec_step(h,count); | ||
512 | return mi_res_simple_running(h); | ||
513 | } | ||
514 | |||
515 | /**[txh]******************************************************************** | ||
516 | |||
517 | Description: | ||
518 | Next line of assembler code. Get inside calls. | ||
519 | |||
520 | Command: -exec-step-instruction | ||
521 | Return: !=0 OK | ||
522 | |||
523 | ***************************************************************************/ | ||
524 | |||
525 | int gmi_exec_step_instruction(mi_h *h) | ||
526 | { | ||
527 | mi_exec_step_instruction(h); | ||
528 | return mi_res_simple_running(h); | ||
529 | } | ||
530 | |||
531 | /**[txh]******************************************************************** | ||
532 | |||
533 | Description: | ||
534 | Execute until location is reached. If file is NULL then is until next | ||
535 | line. | ||
536 | |||
537 | Command: -exec-until | ||
538 | Return: !=0 OK | ||
539 | |||
540 | ***************************************************************************/ | ||
541 | |||
542 | int gmi_exec_until(mi_h *h, const char *file, int line) | ||
543 | { | ||
544 | mi_exec_until(h,file,line); | ||
545 | return mi_res_simple_running(h); | ||
546 | } | ||
547 | |||
548 | /**[txh]******************************************************************** | ||
549 | |||
550 | Description: | ||
551 | Execute until location is reached. | ||
552 | |||
553 | Command: -exec-until (using *address) | ||
554 | Return: !=0 OK | ||
555 | |||
556 | ***************************************************************************/ | ||
557 | |||
558 | int gmi_exec_until_addr(mi_h *h, void *addr) | ||
559 | { | ||
560 | mi_exec_until_addr(h,addr); | ||
561 | return mi_res_simple_running(h); | ||
562 | } | ||
563 | |||
564 | /**[txh]******************************************************************** | ||
565 | |||
566 | Description: | ||
567 | Return to previous frame inmediatly. | ||
568 | |||
569 | Command: -exec-return | ||
570 | Return: A pointer to a new mi_frames structure indicating the current | ||
571 | location. NULL on error. | ||
572 | |||
573 | ***************************************************************************/ | ||
574 | |||
575 | mi_frames *gmi_exec_return(mi_h *h) | ||
576 | { | ||
577 | mi_exec_return(h); | ||
578 | return mi_res_frame(h); | ||
579 | } | ||
580 | |||
581 | /**[txh]******************************************************************** | ||
582 | |||
583 | Description: | ||
584 | Just kill the program. That's what -exec-abort should do, but it isn't | ||
585 | implemented by gdb. This implementation only works if the interactive mode | ||
586 | is disabled (gmi_gdb_set("confirm","off")). | ||
587 | |||
588 | Command: -exec-abort [using kill] | ||
589 | Return: !=0 OK | ||
590 | |||
591 | ***************************************************************************/ | ||
592 | |||
593 | int gmi_exec_kill(mi_h *h) | ||
594 | { | ||
595 | mi_exec_kill(h); | ||
596 | return mi_res_simple_done(h); | ||
597 | } | ||
598 | |||
diff --git a/pathologist/src/mi/gdbmi_stack_man.c b/pathologist/src/mi/gdbmi_stack_man.c new file mode 100644 index 0000000..977ea5e --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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 from | ||
88 | - 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 level and 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 level and args filled. Only for the | ||
121 | frames in the from - 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/pathologist/src/mi/gdbmi_symbol_query.c b/pathologist/src/mi/gdbmi_symbol_query.c new file mode 100644 index 0000000..8910517 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.h" | ||
32 | |||
diff --git a/pathologist/src/mi/gdbmi_target_man.c b/pathologist/src/mi/gdbmi_target_man.c new file mode 100644 index 0000000..bbb2b98 --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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/pathologist/src/mi/gdbmi_thread.c b/pathologist/src/mi/gdbmi_thread.c new file mode 100644 index 0000000..b8fa63d --- /dev/null +++ b/pathologist/src/mi/gdbmi_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 "gdbmi.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/pathologist/src/mi/gdbmi_var_obj.c b/pathologist/src/mi/gdbmi_var_obj.c new file mode 100644 index 0000000..435feec --- /dev/null +++ b/pathologist/src/mi/gdbmi_var_obj.c | |||
@@ -0,0 +1,371 @@ | |||
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. | ||
9 | @<p> | ||
10 | |||
11 | @<pre> | ||
12 | gdb command: Imp? Description: | ||
13 | -var-create Yes create a variable object | ||
14 | -var-delete Yes delete the variable object and its children | ||
15 | -var-set-format Yes set the display format of this variable | ||
16 | -var-show-format Yes show the display format of this variable | ||
17 | -var-info-num-children Yes tells how many children this object has | ||
18 | -var-list-children Yes* return a list of the object's children | ||
19 | -var-info-type Yes show the type of this variable object | ||
20 | -var-info-expression Yes print what this variable object represents | ||
21 | -var-show-attributes Yes is this variable editable? | ||
22 | -var-evaluate-expression Yes get the value of this variable | ||
23 | -var-assign Yes set the value of this variable | ||
24 | -var-update Yes* update the variable and its children | ||
25 | @</pre> | ||
26 | |||
27 | Notes: @<p> | ||
28 | 1) I suggest letting gdb to choose the names for the variables.@* | ||
29 | 2) -var-list-children supports an optional "show values" argument in MI v2. | ||
30 | It isn't implemented.@* | ||
31 | |||
32 | @<p> | ||
33 | |||
34 | * MI v1 and v2 result formats supported. @<p> | ||
35 | |||
36 | ***************************************************************************/ | ||
37 | |||
38 | #include "gdbmi.h" | ||
39 | |||
40 | /* Low level versions. */ | ||
41 | |||
42 | void mi_var_create(mi_h *h, const char *name, int frame, const char *exp) | ||
43 | { | ||
44 | const char *n=name ? name : "-"; | ||
45 | |||
46 | if (frame<0) | ||
47 | mi_send(h,"-var-create %s * %s\n",n,exp); | ||
48 | else | ||
49 | mi_send(h,"-var-create %s %d %s\n",n,frame,exp); | ||
50 | } | ||
51 | |||
52 | void mi_var_delete(mi_h *h, const char *name) | ||
53 | { | ||
54 | mi_send(h,"-var-delete %s\n",name); | ||
55 | } | ||
56 | |||
57 | void mi_var_set_format(mi_h *h, const char *name, const char *format) | ||
58 | { | ||
59 | mi_send(h,"-var-set-format \"%s\" %s\n",name,format); | ||
60 | } | ||
61 | |||
62 | void mi_var_show_format(mi_h *h, const char *name) | ||
63 | { | ||
64 | mi_send(h,"-var-show-format \"%s\"\n",name); | ||
65 | } | ||
66 | |||
67 | void mi_var_info_num_children(mi_h *h, const char *name) | ||
68 | { | ||
69 | mi_send(h,"-var-info-num-children \"%s\"\n",name); | ||
70 | } | ||
71 | |||
72 | void mi_var_info_type(mi_h *h, const char *name) | ||
73 | { | ||
74 | mi_send(h,"-var-info-type \"%s\"\n",name); | ||
75 | } | ||
76 | |||
77 | void mi_var_info_expression(mi_h *h, const char *name) | ||
78 | { | ||
79 | mi_send(h,"-var-info-expression \"%s\"\n",name); | ||
80 | } | ||
81 | |||
82 | void mi_var_show_attributes(mi_h *h, const char *name) | ||
83 | { | ||
84 | mi_send(h,"-var-show-attributes \"%s\"\n",name); | ||
85 | } | ||
86 | |||
87 | void mi_var_update(mi_h *h, const char *name) | ||
88 | { | ||
89 | if (name) | ||
90 | mi_send(h,"-var-update %s\n",name); | ||
91 | else | ||
92 | mi_send(h,"-var-update *\n"); | ||
93 | } | ||
94 | |||
95 | void mi_var_assign(mi_h *h, const char *name, const char *expression) | ||
96 | { | ||
97 | mi_send(h,"-var-assign \"%s\" \"%s\"\n",name,expression); | ||
98 | } | ||
99 | |||
100 | void mi_var_evaluate_expression(mi_h *h, const char *name) | ||
101 | { | ||
102 | mi_send(h,"-var-evaluate-expression \"%s\"\n",name); | ||
103 | } | ||
104 | |||
105 | void mi_var_list_children(mi_h *h, const char *name) | ||
106 | { | ||
107 | if (h->version>=MI_VERSION2U(2,0,0)) | ||
108 | mi_send(h,"-var-list-children --all-values \"%s\"\n",name); | ||
109 | else | ||
110 | mi_send(h,"-var-list-children \"%s\"\n",name); | ||
111 | } | ||
112 | |||
113 | /* High level versions. */ | ||
114 | |||
115 | /**[txh]******************************************************************** | ||
116 | |||
117 | Description: | ||
118 | Create a variable object. I recommend using gmi_var_create and letting | ||
119 | gdb choose the names. | ||
120 | |||
121 | Command: -var-create | ||
122 | Return: A new mi_gvar strcture or NULL on error. | ||
123 | |||
124 | ***************************************************************************/ | ||
125 | |||
126 | mi_gvar *gmi_var_create_nm(mi_h *h, const char *name, int frame, const char *exp) | ||
127 | { | ||
128 | mi_var_create(h,name,frame,exp); | ||
129 | return mi_res_gvar(h,NULL,exp); | ||
130 | } | ||
131 | |||
132 | /**[txh]******************************************************************** | ||
133 | |||
134 | Description: | ||
135 | Create a variable object. The name is selected by gdb. Alternative: | ||
136 | gmi_full_var_create. | ||
137 | |||
138 | Command: -var-create [auto name] | ||
139 | Return: A new mi_gvar strcture or NULL on error. | ||
140 | |||
141 | ***************************************************************************/ | ||
142 | |||
143 | mi_gvar *gmi_var_create(mi_h *h, int frame, const char *exp) | ||
144 | { | ||
145 | return gmi_var_create_nm(h,NULL,frame,exp); | ||
146 | } | ||
147 | |||
148 | /**[txh]******************************************************************** | ||
149 | |||
150 | Description: | ||
151 | Delete a variable object. Doesn't free the mi_gvar data. | ||
152 | |||
153 | Command: -var-delete | ||
154 | Return: !=0 OK | ||
155 | |||
156 | ***************************************************************************/ | ||
157 | |||
158 | int gmi_var_delete(mi_h *h, mi_gvar *var) | ||
159 | { | ||
160 | mi_var_delete(h,var->name); | ||
161 | return mi_res_simple_done(h); | ||
162 | } | ||
163 | |||
164 | /**[txh]******************************************************************** | ||
165 | |||
166 | Description: | ||
167 | Set the format used to represent the result. | ||
168 | |||
169 | Command: -var-set-format | ||
170 | Return: !=0 OK | ||
171 | |||
172 | ***************************************************************************/ | ||
173 | |||
174 | int gmi_var_set_format(mi_h *h, mi_gvar *var, enum mi_gvar_fmt format) | ||
175 | { | ||
176 | int ret; | ||
177 | |||
178 | mi_var_set_format(h,var->name,mi_format_enum_to_str(format)); | ||
179 | ret=mi_res_simple_done(h); | ||
180 | if (ret) | ||
181 | var->format=format; | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | /**[txh]******************************************************************** | ||
186 | |||
187 | Description: | ||
188 | Fill the format field with info from gdb. | ||
189 | |||
190 | Command: -var-show-format | ||
191 | Return: !=0 OK. | ||
192 | |||
193 | ***************************************************************************/ | ||
194 | |||
195 | int gmi_var_show_format(mi_h *h, mi_gvar *var) | ||
196 | { | ||
197 | mi_var_show_format(h,var->name); | ||
198 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
199 | } | ||
200 | |||
201 | /**[txh]******************************************************************** | ||
202 | |||
203 | Description: | ||
204 | Fill the numchild field with info from gdb. | ||
205 | |||
206 | Command: -var-info-num-children | ||
207 | Return: !=0 OK | ||
208 | |||
209 | ***************************************************************************/ | ||
210 | |||
211 | int gmi_var_info_num_children(mi_h *h, mi_gvar *var) | ||
212 | { | ||
213 | mi_var_info_num_children(h,var->name); | ||
214 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
215 | } | ||
216 | |||
217 | /**[txh]******************************************************************** | ||
218 | |||
219 | Description: | ||
220 | Fill the type field with info from gdb. | ||
221 | |||
222 | Command: -var-info-type | ||
223 | Return: !=0 OK | ||
224 | |||
225 | ***************************************************************************/ | ||
226 | |||
227 | int gmi_var_info_type(mi_h *h, mi_gvar *var) | ||
228 | { | ||
229 | mi_var_info_type(h,var->name); | ||
230 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
231 | } | ||
232 | |||
233 | /**[txh]******************************************************************** | ||
234 | |||
235 | Description: | ||
236 | Fill the expression and lang fields with info from gdb. Note that lang | ||
237 | isn't filled during creation. | ||
238 | |||
239 | Command: -var-info-expression | ||
240 | Return: !=0 OK | ||
241 | |||
242 | ***************************************************************************/ | ||
243 | |||
244 | int gmi_var_info_expression(mi_h *h, mi_gvar *var) | ||
245 | { | ||
246 | mi_var_info_expression(h,var->name); | ||
247 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
248 | } | ||
249 | |||
250 | |||
251 | /**[txh]******************************************************************** | ||
252 | |||
253 | Description: | ||
254 | Fill the attr field with info from gdb. Note that attr isn't filled | ||
255 | during creation. | ||
256 | |||
257 | Command: -var-show-attributes | ||
258 | Return: !=0 OK | ||
259 | |||
260 | ***************************************************************************/ | ||
261 | |||
262 | int gmi_var_show_attributes(mi_h *h, mi_gvar *var) | ||
263 | { | ||
264 | mi_var_show_attributes(h,var->name); | ||
265 | return mi_res_gvar(h,var,NULL)!=NULL; | ||
266 | } | ||
267 | |||
268 | /**[txh]******************************************************************** | ||
269 | |||
270 | Description: | ||
271 | Create the variable and also fill the lang and attr fields. The name is | ||
272 | selected by gdb. | ||
273 | |||
274 | Command: -var-create + -var-info-expression + -var-show-attributes | ||
275 | Return: A new mi_gvar strcture or NULL on error. | ||
276 | |||
277 | ***************************************************************************/ | ||
278 | |||
279 | mi_gvar *gmi_full_var_create(mi_h *h, int frame, const char *exp) | ||
280 | { | ||
281 | mi_gvar *var=gmi_var_create_nm(h,NULL,frame,exp); | ||
282 | if (var) | ||
283 | {/* What if it fails? */ | ||
284 | gmi_var_info_expression(h,var); | ||
285 | gmi_var_show_attributes(h,var); | ||
286 | } | ||
287 | return var; | ||
288 | } | ||
289 | |||
290 | /**[txh]******************************************************************** | ||
291 | |||
292 | Description: | ||
293 | Update variable. Use NULL for all. Note that *changed can be NULL if none | ||
294 | updated. | ||
295 | |||
296 | Command: -var-update | ||
297 | Return: !=0 OK. The changed list contains the list of changed vars. | ||
298 | |||
299 | ***************************************************************************/ | ||
300 | |||
301 | int gmi_var_update(mi_h *h, mi_gvar *var, mi_gvar_chg **changed) | ||
302 | { | ||
303 | mi_var_update(h,var ? var->name : NULL); | ||
304 | return mi_res_changelist(h,changed); | ||
305 | } | ||
306 | |||
307 | /**[txh]******************************************************************** | ||
308 | |||
309 | Description: | ||
310 | Change variable. The new value replaces the value field. | ||
311 | |||
312 | Command: -var-assign | ||
313 | Return: !=0 OK | ||
314 | |||
315 | ***************************************************************************/ | ||
316 | |||
317 | int gmi_var_assign(mi_h *h, mi_gvar *var, const char *expression) | ||
318 | { | ||
319 | char *res; | ||
320 | mi_var_assign(h,var->name,expression); | ||
321 | res=mi_res_value(h); | ||
322 | if (res) | ||
323 | { | ||
324 | free(var->value); | ||
325 | var->value=res; | ||
326 | return 1; | ||
327 | } | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /**[txh]******************************************************************** | ||
332 | |||
333 | Description: | ||
334 | Fill the value field getting the current value for a variable. | ||
335 | |||
336 | Command: -var-evaluate-expression | ||
337 | Return: !=0 OK, value contains the result. | ||
338 | |||
339 | ***************************************************************************/ | ||
340 | |||
341 | int gmi_var_evaluate_expression(mi_h *h, mi_gvar *var) | ||
342 | { | ||
343 | char *s; | ||
344 | |||
345 | mi_var_evaluate_expression(h,var->name); | ||
346 | s=mi_res_value(h); | ||
347 | if (s) | ||
348 | { | ||
349 | free(var->value); | ||
350 | var->value=s; | ||
351 | } | ||
352 | return s!=NULL; | ||
353 | } | ||
354 | |||
355 | /**[txh]******************************************************************** | ||
356 | |||
357 | Description: | ||
358 | List children. It ONLY returns the first level information. :-(@* | ||
359 | On success the child field contains the list of children. | ||
360 | |||
361 | Command: -var-list-children | ||
362 | Return: !=0 OK | ||
363 | |||
364 | ***************************************************************************/ | ||
365 | |||
366 | int gmi_var_list_children(mi_h *h, mi_gvar *var) | ||
367 | { | ||
368 | mi_var_list_children(h,var->name); | ||
369 | return mi_res_children(h,var); | ||
370 | } | ||
371 | |||