diff options
author | Safey A.Halim <safey.allah@gmail.com> | 2010-06-29 19:14:37 +0000 |
---|---|---|
committer | Safey A.Halim <safey.allah@gmail.com> | 2010-06-29 19:14:37 +0000 |
commit | 1e2ec555753751d032dbe1f19d5206a2da4de65a (patch) | |
tree | ba5f6d9487ab2f7aea7fa98d1de3d6abef268b33 /src/monkey/cpp_int.cc | |
parent | c75af8bb6df74bfcba1e7b1a3123babf066a1939 (diff) | |
download | gnunet-1e2ec555753751d032dbe1f19d5206a2da4de65a.tar.gz gnunet-1e2ec555753751d032dbe1f19d5206a2da4de65a.zip |
Adding "Monkey", GNUnet module for automatic debugging. Experimental code so far.
Diffstat (limited to 'src/monkey/cpp_int.cc')
-rw-r--r-- | src/monkey/cpp_int.cc | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/src/monkey/cpp_int.cc b/src/monkey/cpp_int.cc new file mode 100644 index 000000000..815d6f7b7 --- /dev/null +++ b/src/monkey/cpp_int.cc | |||
@@ -0,0 +1,1123 @@ | |||
1 | /**[txh]******************************************************************** | ||
2 | |||
3 | Copyright (c) 2004-2007 by Salvador E. Tropea. | ||
4 | Covered by the GPL license. | ||
5 | |||
6 | Module: C++ Interface. | ||
7 | Comments: | ||
8 | Implements a very simple (naive ;-) C++ wrapper.@p | ||
9 | |||
10 | ***************************************************************************/ | ||
11 | |||
12 | #include <string.h> | ||
13 | #include <limits.h> | ||
14 | #include "mi_gdb.h" | ||
15 | |||
16 | /**[txh]******************************************************************** | ||
17 | |||
18 | Description: | ||
19 | Initializes a debugger object. It starts in the "disconnected" state. | ||
20 | Use @x{::Connect} after it. | ||
21 | |||
22 | ***************************************************************************/ | ||
23 | |||
24 | MIDebugger::MIDebugger() | ||
25 | { | ||
26 | state=disconnected; | ||
27 | h=NULL; | ||
28 | aux_tty=NULL; | ||
29 | waitingTempBkpt=0; | ||
30 | targetEndian=enUnknown; | ||
31 | targetArch=arUnknown; | ||
32 | } | ||
33 | |||
34 | /**[txh]******************************************************************** | ||
35 | |||
36 | Description: | ||
37 | This is the destructor for the class. It tries to change the state to | ||
38 | "disconnected" doing the needed actions. | ||
39 | |||
40 | ***************************************************************************/ | ||
41 | |||
42 | MIDebugger::~MIDebugger() | ||
43 | { | ||
44 | if (state==running) | ||
45 | { | ||
46 | Stop(); | ||
47 | mi_stop *rs; | ||
48 | // TODO: Some kind of time-out | ||
49 | while (!Poll(rs)); | ||
50 | mi_free_stop(rs); | ||
51 | state=stopped; | ||
52 | } | ||
53 | if (state==stopped) | ||
54 | { | ||
55 | Kill(); | ||
56 | state=target_specified; | ||
57 | } | ||
58 | if (state==target_specified) | ||
59 | { | ||
60 | TargetUnselect(); | ||
61 | state=connected; | ||
62 | } | ||
63 | if (state==connected) | ||
64 | Disconnect(); | ||
65 | // Here state==disconnected | ||
66 | } | ||
67 | |||
68 | /**[txh]******************************************************************** | ||
69 | |||
70 | Description: | ||
71 | Connects to gdb. Currently only local connections are supported, that's | ||
72 | a gdb limitation. Call it when in "unconnected" state, on success it will | ||
73 | change to the "connected" state. After it you should call one of the | ||
74 | SelectTarget members. @x{::SelectTargetX11}, @x{::SelectTargetLinux} or | ||
75 | @x{::SelectTargetRemote}. To finish the connection use @x{::Disconnect}. | ||
76 | |||
77 | Return: !=0 OK. | ||
78 | |||
79 | ***************************************************************************/ | ||
80 | |||
81 | int MIDebugger::Connect(bool ) | ||
82 | { | ||
83 | if (state==disconnected) | ||
84 | { | ||
85 | h=mi_connect_local(); | ||
86 | if (h!=NULL) | ||
87 | { | ||
88 | state=connected; | ||
89 | return 1; | ||
90 | } | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | /**[txh]******************************************************************** | ||
96 | |||
97 | Description: | ||
98 | Finishes the connection to gdb. Call when in "connected" state, on success | ||
99 | it will change to "disconnected" state. This function first tries to exit | ||
100 | from gdb and then close the connection. But if gdb fails to exit it will be | ||
101 | killed. | ||
102 | |||
103 | Return: !=0 OK | ||
104 | |||
105 | ***************************************************************************/ | ||
106 | |||
107 | int MIDebugger::Disconnect() | ||
108 | { | ||
109 | if (state==connected) | ||
110 | { | ||
111 | gmi_gdb_exit(h); | ||
112 | mi_disconnect(h); | ||
113 | state=disconnected; | ||
114 | return 1; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /**[txh]******************************************************************** | ||
120 | |||
121 | Description: | ||
122 | Protected member that implements @x{::SelectTargetX11} and | ||
123 | @x{::SelectTargetLinux}. | ||
124 | |||
125 | Return: !=0 OK. | ||
126 | |||
127 | ***************************************************************************/ | ||
128 | |||
129 | int MIDebugger::SelectTargetTTY(const char *exec, const char *args, | ||
130 | const char *auxtty, dMode m) | ||
131 | { | ||
132 | if (state!=connected) | ||
133 | return 0; | ||
134 | |||
135 | targetEndian=enUnknown; | ||
136 | targetArch=arUnknown; | ||
137 | mode=m; | ||
138 | if (!gmi_set_exec(h,exec,args)) | ||
139 | return 0; | ||
140 | |||
141 | const char *tty_name; | ||
142 | #ifndef __CYGWIN__ | ||
143 | if (!auxtty) | ||
144 | { | ||
145 | aux_tty=m==dmLinux ? gmi_look_for_free_vt() : gmi_start_xterm(); | ||
146 | if (!aux_tty) | ||
147 | return 0; | ||
148 | tty_name=aux_tty->tty; | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | tty_name=auxtty; | ||
153 | } | ||
154 | if (!gmi_target_terminal(h,tty_name)) | ||
155 | return 0; | ||
156 | #else | ||
157 | tty_name=NULL; | ||
158 | if (!gmi_gdb_set(h,"new-console","on")) | ||
159 | return 0; | ||
160 | #endif | ||
161 | |||
162 | state=target_specified; | ||
163 | preRun=false; | ||
164 | return 1; | ||
165 | } | ||
166 | |||
167 | /**[txh]******************************************************************** | ||
168 | |||
169 | Description: | ||
170 | Starts a debug session for X11. It opens an xterm console for the program | ||
171 | to debug and tells gdb which executable to debug and the command line | ||
172 | options to pass. You can specify an already existing tty console to be used. | ||
173 | Can be called when the state is "connected". On success will change to the | ||
174 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
175 | to define breakpoints and similar stuff. To finish it use | ||
176 | @x{::TargetUnselect}. | ||
177 | |||
178 | Return: !=0 OK. | ||
179 | |||
180 | ***************************************************************************/ | ||
181 | |||
182 | int MIDebugger::SelectTargetX11(const char *exec, const char *args, | ||
183 | const char *auxtty) | ||
184 | { | ||
185 | return SelectTargetTTY(exec,args,auxtty,dmX11); | ||
186 | } | ||
187 | |||
188 | |||
189 | /**[txh]******************************************************************** | ||
190 | |||
191 | Description: | ||
192 | Starts a debug session for Linux console. It selects an empty VT for the | ||
193 | program to debug and tells gdb which executable to debug and the command line | ||
194 | options to pass. You can specify an already existing tty console to be used. | ||
195 | Can be called when the state is "connected". On success will change to the | ||
196 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
197 | to define breakpoints and similar stuff. To finish it use | ||
198 | @x{::TargetUnselect}. | ||
199 | |||
200 | Return: !=0 OK. | ||
201 | |||
202 | ***************************************************************************/ | ||
203 | |||
204 | int MIDebugger::SelectTargetLinux(const char *exec, const char *args, | ||
205 | const char *auxtty) | ||
206 | { | ||
207 | return SelectTargetTTY(exec,args,auxtty,dmLinux); | ||
208 | } | ||
209 | |||
210 | /**[txh]******************************************************************** | ||
211 | |||
212 | Description: | ||
213 | Starts a remote session. The other end should be running gdbserver. You | ||
214 | must specify a local copy of the program to debug with debug info. The remote | ||
215 | copy can be stripped. The @var{rtype} and @var{rparams} selects the protocol | ||
216 | and the remote machine. Read gdb docs to know more about the available | ||
217 | options. If @var{rtype} is omitted "extended-remote" protocol is used. | ||
218 | Can be called when the state is "connected". On success will change to the | ||
219 | "target_specified" state. After it you can use @x{::Run} or use the members | ||
220 | to define breakpoints and similar stuff. To finish it use | ||
221 | @x{::TargetUnselect}. Note that when gdb uses remote debugging the remote | ||
222 | program starts running. The @x{::Run} member knows about it. | ||
223 | |||
224 | Return: !=0 OK. | ||
225 | Example: | ||
226 | o->SelectTargetRemote("./exec_file","192.168.1.65:5000"); | ||
227 | |||
228 | ***************************************************************************/ | ||
229 | |||
230 | int MIDebugger::SelectTargetRemote(const char *exec, const char *rparams, | ||
231 | const char *rtype, bool download) | ||
232 | { | ||
233 | if (state!=connected) | ||
234 | return 0; | ||
235 | |||
236 | mode=dmRemote; | ||
237 | preRun=true; | ||
238 | targetEndian=enUnknown; | ||
239 | targetArch=arUnknown; | ||
240 | if (rtype==NULL) | ||
241 | rtype="extended-remote"; | ||
242 | |||
243 | /* Tell gdb to load symbols from the local copy. */ | ||
244 | int res=download ? gmi_set_exec(h,exec,NULL) : gmi_file_symbol_file(h,exec); | ||
245 | if (!res) | ||
246 | return 0; | ||
247 | /* Select the target */ | ||
248 | if (!gmi_target_select(h,rtype,rparams)) | ||
249 | return 0; | ||
250 | /* Download the binary */ | ||
251 | if (download) | ||
252 | { | ||
253 | if (!gmi_target_download(h)) | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | state=target_specified; | ||
258 | return 1; | ||
259 | } | ||
260 | |||
261 | /**[txh]******************************************************************** | ||
262 | |||
263 | Description: | ||
264 | Starts a local session using an already running process. | ||
265 | |||
266 | Return: !=0 OK. | ||
267 | |||
268 | ***************************************************************************/ | ||
269 | |||
270 | mi_frames *MIDebugger::SelectTargetPID(const char *exec, int pid) | ||
271 | { | ||
272 | if (state!=connected) | ||
273 | return NULL; | ||
274 | |||
275 | mode=dmPID; | ||
276 | preRun=false; | ||
277 | targetEndian=enUnknown; | ||
278 | targetArch=arUnknown; | ||
279 | |||
280 | mi_frames *res=gmi_target_attach(h,pid); | ||
281 | if (res) | ||
282 | { | ||
283 | state=stopped; | ||
284 | |||
285 | /* Tell gdb to load symbols from the local copy. */ | ||
286 | if (!gmi_file_symbol_file(h,exec)) | ||
287 | { | ||
288 | mi_free_frames(res); | ||
289 | return NULL; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | return res; | ||
294 | } | ||
295 | |||
296 | /**[txh]******************************************************************** | ||
297 | |||
298 | Description: | ||
299 | Used to unselect the current target. When X11 mode it closes the auxiliar | ||
300 | terminal. For remote debugging it uses "detach". Can be called when in | ||
301 | "target_specified" state. On success it changes to "connected" state. | ||
302 | |||
303 | Return: !=0 OK | ||
304 | |||
305 | ***************************************************************************/ | ||
306 | |||
307 | int MIDebugger::TargetUnselect() | ||
308 | { | ||
309 | switch (mode) | ||
310 | { | ||
311 | case dmX11: | ||
312 | case dmLinux: | ||
313 | if (state!=target_specified) | ||
314 | return 0; | ||
315 | if (aux_tty) | ||
316 | { | ||
317 | gmi_end_aux_term(aux_tty); | ||
318 | aux_tty=NULL; | ||
319 | } | ||
320 | break; | ||
321 | case dmPID: | ||
322 | case dmRemote: | ||
323 | if (state!=target_specified) | ||
324 | { | ||
325 | if (state!=stopped || !gmi_target_detach(h)) | ||
326 | return 0; | ||
327 | } | ||
328 | break; | ||
329 | } | ||
330 | state=connected; | ||
331 | return 1; | ||
332 | } | ||
333 | |||
334 | /**[txh]******************************************************************** | ||
335 | |||
336 | Description: | ||
337 | Starts running the program. You should set breakpoint before it. Can be | ||
338 | called when state is "target_specified". On success will change to "running" | ||
339 | state. After it you should poll for async responses using @x{::Poll}. The | ||
340 | program can stop for many reasons asynchronously and also exit. This | ||
341 | information is known using Poll. You can stop the program using @x{::Stop}. | ||
342 | |||
343 | Return: !=0 OK. | ||
344 | |||
345 | ***************************************************************************/ | ||
346 | |||
347 | int MIDebugger::Run() | ||
348 | { | ||
349 | if (state!=target_specified) | ||
350 | return 0; | ||
351 | |||
352 | int res; | ||
353 | if (preRun) | ||
354 | res=gmi_exec_continue(h); | ||
355 | else | ||
356 | res=gmi_exec_run(h); | ||
357 | if (res) | ||
358 | state=running; | ||
359 | |||
360 | return res; | ||
361 | } | ||
362 | |||
363 | /**[txh]******************************************************************** | ||
364 | |||
365 | Description: | ||
366 | Stops the program execution. GDB sends an interrupt signal to the program. | ||
367 | Can be called when the state is "running". It won't switch to "stopped" | ||
368 | state automatically. Instead you must poll for async events and wait for a | ||
369 | stopped notification. After it you can call @x{::Continue} to resume | ||
370 | execution. | ||
371 | |||
372 | Return: | ||
373 | Example: !=0 OK | ||
374 | |||
375 | ***************************************************************************/ | ||
376 | |||
377 | int MIDebugger::Stop() | ||
378 | { | ||
379 | if (state!=running) | ||
380 | return 0; | ||
381 | return gmi_exec_interrupt(h); | ||
382 | } | ||
383 | |||
384 | /**[txh]******************************************************************** | ||
385 | |||
386 | Description: | ||
387 | Polls gdb looking for async responses. Currently it just looks for | ||
388 | "stopped" messages. You must call it when the state is "running". But the | ||
389 | function will poll gdb even if the state isn't "running". When a stopped | ||
390 | message is received the state changes to stopped or target_specified (the | ||
391 | last is when we get some exit). | ||
392 | |||
393 | Return: !=0 if we got a response. The @var{rs} pointer will point to an | ||
394 | mi_stop structure if we got it or will be NULL if we didn't. | ||
395 | |||
396 | ***************************************************************************/ | ||
397 | |||
398 | int MIDebugger::Poll(mi_stop *&rs) | ||
399 | { | ||
400 | if (state==disconnected || !mi_get_response(h)) | ||
401 | return 0; | ||
402 | |||
403 | mi_stop *res=mi_res_stop(h); | ||
404 | if (res) | ||
405 | { | ||
406 | if (res->reason==sr_exited_signalled || | ||
407 | res->reason==sr_exited || | ||
408 | res->reason==sr_exited_normally) | ||
409 | // When we use a PID the exit makes it invalid, so we don't have a | ||
410 | // valid target to re-run. | ||
411 | state=mode==dmPID ? connected : target_specified; | ||
412 | else | ||
413 | state=stopped; | ||
414 | if (res->reason==sr_unknown && waitingTempBkpt) | ||
415 | { | ||
416 | waitingTempBkpt=0; | ||
417 | res->reason=sr_bkpt_hit; | ||
418 | } | ||
419 | } | ||
420 | else | ||
421 | {// We got an error. It looks like most async commands returns running even | ||
422 | // before they are sure the process is running. Latter we get the real | ||
423 | // error. So I'm assuming the program is stopped. | ||
424 | // Lamentably -target-exec-status isn't implemented and even in this case | ||
425 | // if the program is really running as real async isn't implemented it | ||
426 | // will fail anyways. | ||
427 | if (state==running) | ||
428 | state=stopped; | ||
429 | } | ||
430 | rs=res; | ||
431 | return 1; | ||
432 | } | ||
433 | |||
434 | /**[txh]******************************************************************** | ||
435 | |||
436 | Description: | ||
437 | Resumes execution after the program "stopped". Can be called when the state | ||
438 | is stopped. On success will change to "running" state. | ||
439 | |||
440 | Return: !=0 OK | ||
441 | |||
442 | ***************************************************************************/ | ||
443 | |||
444 | int MIDebugger::Continue() | ||
445 | { | ||
446 | if (state!=stopped) | ||
447 | return 0; | ||
448 | int res=gmi_exec_continue(h); | ||
449 | if (res) | ||
450 | state=running; | ||
451 | return res; | ||
452 | } | ||
453 | |||
454 | /**[txh]******************************************************************** | ||
455 | |||
456 | Description: | ||
457 | Starts program execution or resumes it. When the state is target_specified | ||
458 | it calls @x{::Run} otherwise it uses @x{::Continue}. Can be called when the | ||
459 | state is "target_specified" or "stopped". On success will change to | ||
460 | "running" state. | ||
461 | |||
462 | Return: !=0 OK | ||
463 | |||
464 | ***************************************************************************/ | ||
465 | |||
466 | int MIDebugger::RunOrContinue() | ||
467 | { | ||
468 | if (state==target_specified) | ||
469 | return Run(); | ||
470 | return Continue(); | ||
471 | } | ||
472 | |||
473 | /**[txh]******************************************************************** | ||
474 | |||
475 | Description: | ||
476 | Kills the program you are debugging. Can be called when the state is | ||
477 | "stopped" or "running". On success changes the state to "target_specified". | ||
478 | Note that if you want to restart the program you can just call @x{::Run} and | ||
479 | if you want to just stop the program call @x{::Stop}. | ||
480 | |||
481 | Return: !=0 OK | ||
482 | |||
483 | ***************************************************************************/ | ||
484 | |||
485 | int MIDebugger::Kill() | ||
486 | { | ||
487 | if (state!=stopped && state!=running) | ||
488 | return 0; | ||
489 | /* GDB/MI doesn't implement it (yet), so we use the regular kill. */ | ||
490 | /* Ensure confirm is off. */ | ||
491 | char *prev=gmi_gdb_show(h,"confirm"); | ||
492 | if (!prev) | ||
493 | return 0; | ||
494 | if (strcmp(prev,"off")) | ||
495 | { | ||
496 | if (!gmi_gdb_set(h,"confirm","off")) | ||
497 | { | ||
498 | free(prev); | ||
499 | return 0; | ||
500 | } | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | free(prev); | ||
505 | prev=NULL; | ||
506 | } | ||
507 | /* Do the kill. */ | ||
508 | int res=gmi_exec_kill(h); | ||
509 | /* Revert confirm option if needed. */ | ||
510 | if (prev) | ||
511 | { | ||
512 | gmi_gdb_set(h,"confirm",prev); | ||
513 | free(prev); | ||
514 | } | ||
515 | |||
516 | if (res) | ||
517 | state=target_specified; | ||
518 | |||
519 | return res; | ||
520 | } | ||
521 | |||
522 | /**[txh]******************************************************************** | ||
523 | |||
524 | Description: | ||
525 | Inserts a breakpoint at @var{file} and @var{line}. Can be called when the | ||
526 | state is "stopped" or "target_specified". | ||
527 | |||
528 | Return: An mi_bkpt structure or NULL if error. | ||
529 | |||
530 | ***************************************************************************/ | ||
531 | |||
532 | mi_bkpt *MIDebugger::Breakpoint(const char *file, int line) | ||
533 | { | ||
534 | if (state!=stopped && state!=target_specified) | ||
535 | return NULL; | ||
536 | return gmi_break_insert(h,file,line); | ||
537 | } | ||
538 | |||
539 | /**[txh]******************************************************************** | ||
540 | |||
541 | Description: | ||
542 | Inserts a breakpoint at @var{where}, all options available. Can be called | ||
543 | when the state is "stopped" or "target_specified". | ||
544 | |||
545 | Return: An mi_bkpt structure or NULL if error. | ||
546 | |||
547 | ***************************************************************************/ | ||
548 | |||
549 | mi_bkpt *MIDebugger::Breakpoint(const char *where, bool temporary, | ||
550 | const char *cond, int count, int thread, | ||
551 | bool hard_assist) | ||
552 | { | ||
553 | if (state!=stopped && state!=target_specified) | ||
554 | return NULL; | ||
555 | return gmi_break_insert_full(h,temporary,hard_assist,cond,count,thread,where); | ||
556 | } | ||
557 | |||
558 | |||
559 | const int maxWhere=PATH_MAX+256; | ||
560 | |||
561 | mi_bkpt *MIDebugger::Breakpoint(mi_bkpt *b) | ||
562 | { | ||
563 | if (state!=stopped && state!=target_specified) | ||
564 | return NULL; | ||
565 | |||
566 | char buf[maxWhere]; | ||
567 | buf[0]=0; | ||
568 | switch (b->mode) | ||
569 | { | ||
570 | case m_file_line: | ||
571 | snprintf(buf,maxWhere,"%s:%d",b->file,b->line); | ||
572 | break; | ||
573 | case m_function: | ||
574 | snprintf(buf,maxWhere,"%s",b->func); | ||
575 | break; | ||
576 | case m_file_function: | ||
577 | snprintf(buf,maxWhere,"%s:%s",b->file,b->func); | ||
578 | break; | ||
579 | case m_address: | ||
580 | snprintf(buf,maxWhere,"*%p",b->addr); | ||
581 | break; | ||
582 | } | ||
583 | return Breakpoint(buf,b->disp==d_del,b->cond,b->ignore,b->thread, | ||
584 | b->type==t_hw); | ||
585 | } | ||
586 | |||
587 | /**[txh]******************************************************************** | ||
588 | |||
589 | Description: | ||
590 | Inserts a breakpoint at @var{file} and @var{line} all options available. | ||
591 | Can be called when the state is "stopped" or "target_specified". | ||
592 | |||
593 | Return: An mi_bkpt structure or NULL if error. | ||
594 | |||
595 | ***************************************************************************/ | ||
596 | |||
597 | mi_bkpt *MIDebugger::BreakpointFull(const char *file, int line, | ||
598 | bool temporary, const char *cond, | ||
599 | int count, int thread, bool hard_assist) | ||
600 | { | ||
601 | if (state!=stopped && state!=target_specified) | ||
602 | return NULL; | ||
603 | return gmi_break_insert_full_fl(h,file,line,temporary,hard_assist,cond, | ||
604 | count,thread); | ||
605 | } | ||
606 | |||
607 | /**[txh]******************************************************************** | ||
608 | |||
609 | Description: | ||
610 | Removes the specified breakpoint. It doesn't free the structure. Can be | ||
611 | called when the state is "stopped" or "target_specified". | ||
612 | |||
613 | Return: !=0 OK | ||
614 | |||
615 | ***************************************************************************/ | ||
616 | |||
617 | int MIDebugger::BreakDelete(mi_bkpt *b) | ||
618 | { | ||
619 | if ((state!=stopped && state!=target_specified) || !b) | ||
620 | return 0; | ||
621 | return gmi_break_delete(h,b->number); | ||
622 | } | ||
623 | |||
624 | /**[txh]******************************************************************** | ||
625 | |||
626 | Description: | ||
627 | Inserts a watchpoint for the specified expression. Can be called when the | ||
628 | state is "stopped" or "target_specified". | ||
629 | |||
630 | Return: An mi_wp structure or NULL if error. | ||
631 | |||
632 | ***************************************************************************/ | ||
633 | |||
634 | mi_wp *MIDebugger::Watchpoint(enum mi_wp_mode mode, const char *exp) | ||
635 | { | ||
636 | if (state!=stopped && state!=target_specified) | ||
637 | return NULL; | ||
638 | return gmi_break_watch(h,mode,exp); | ||
639 | } | ||
640 | |||
641 | /**[txh]******************************************************************** | ||
642 | |||
643 | Description: | ||
644 | Removes the specified watchpoint. It doesn't free the structure. Can be | ||
645 | called when the state is "stopped" or "target_specified". | ||
646 | |||
647 | Return: !=0 OK | ||
648 | |||
649 | ***************************************************************************/ | ||
650 | |||
651 | int MIDebugger::WatchDelete(mi_wp *w) | ||
652 | { | ||
653 | if ((state!=stopped && state!=target_specified) || !w) | ||
654 | return 0; | ||
655 | return gmi_break_delete(h,w->number); | ||
656 | } | ||
657 | |||
658 | /**[txh]******************************************************************** | ||
659 | |||
660 | Description: | ||
661 | Puts a temporal breakpoint in main function and starts running. Can be | ||
662 | called when the state is "target_specified". If successful the state will | ||
663 | change to "running". | ||
664 | |||
665 | Return: !=0 OK | ||
666 | |||
667 | ***************************************************************************/ | ||
668 | |||
669 | int MIDebugger::RunToMain() | ||
670 | { | ||
671 | if (state!=target_specified) | ||
672 | return 0; | ||
673 | mi_bkpt *b=Breakpoint(mi_get_main_func(),true); | ||
674 | if (!b) | ||
675 | return 0; | ||
676 | mi_free_bkpt(b); | ||
677 | waitingTempBkpt=1; | ||
678 | return Run(); | ||
679 | } | ||
680 | |||
681 | /**[txh]******************************************************************** | ||
682 | |||
683 | Description: | ||
684 | Executes upto the next line, doesn't follow function calls. The @var{inst} | ||
685 | argument is for assembler. If the state is "target_specified" it will go to | ||
686 | the first line in the main function. If the state is "stopped" will use the | ||
687 | next command. If successfully the state will change to "running". | ||
688 | |||
689 | Return: !=0 OK | ||
690 | |||
691 | ***************************************************************************/ | ||
692 | |||
693 | int MIDebugger::StepOver(bool inst) | ||
694 | { | ||
695 | int res=0; | ||
696 | |||
697 | if (state==target_specified) | ||
698 | {// We aren't running | ||
699 | // Walk to main | ||
700 | return RunToMain(); | ||
701 | } | ||
702 | if (state==stopped) | ||
703 | { | ||
704 | if (inst) | ||
705 | res=gmi_exec_next_instruction(h); | ||
706 | else | ||
707 | res=gmi_exec_next(h); | ||
708 | if (res) | ||
709 | state=running; | ||
710 | } | ||
711 | return res; | ||
712 | } | ||
713 | |||
714 | /**[txh]******************************************************************** | ||
715 | |||
716 | Description: | ||
717 | Executes until the specified point. If the state is "target_specified" it | ||
718 | uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. | ||
719 | Fails for any other state. | ||
720 | |||
721 | Return: !=0 OK | ||
722 | |||
723 | ***************************************************************************/ | ||
724 | |||
725 | int MIDebugger::GoTo(const char *file, int line) | ||
726 | { | ||
727 | int res=0; | ||
728 | |||
729 | if (state==target_specified) | ||
730 | {// We aren't running | ||
731 | // Use a temporal breakpoint | ||
732 | int l=strlen(file)+32; | ||
733 | char buf[l]; | ||
734 | snprintf(buf,l,"%s:%d",file,line); | ||
735 | mi_bkpt *b=Breakpoint(buf,true); | ||
736 | if (b) | ||
737 | { | ||
738 | mi_free_bkpt(b); | ||
739 | waitingTempBkpt=1; | ||
740 | res=Run(); | ||
741 | } | ||
742 | } | ||
743 | else if (state==stopped) | ||
744 | { | ||
745 | res=gmi_exec_until(h,file,line); | ||
746 | if (res) | ||
747 | state=running; | ||
748 | } | ||
749 | return res; | ||
750 | } | ||
751 | |||
752 | /**[txh]******************************************************************** | ||
753 | |||
754 | Description: | ||
755 | Executes until the specified point. If the state is "target_specified" it | ||
756 | uses a temporal breakpoint. If the state is "stopped" it uses -exec-until. | ||
757 | Fails for any other state. | ||
758 | |||
759 | Return: !=0 OK | ||
760 | |||
761 | ***************************************************************************/ | ||
762 | |||
763 | int MIDebugger::GoTo(void *addr) | ||
764 | { | ||
765 | int res=0; | ||
766 | |||
767 | if (state==target_specified) | ||
768 | {// We aren't running | ||
769 | // Use a temporal breakpoint | ||
770 | char buf[32]; | ||
771 | snprintf(buf,32,"*%p",addr); | ||
772 | mi_bkpt *b=Breakpoint(buf,true); | ||
773 | if (b) | ||
774 | { | ||
775 | mi_free_bkpt(b); | ||
776 | waitingTempBkpt=1; | ||
777 | res=Run(); | ||
778 | } | ||
779 | } | ||
780 | else if (state==stopped) | ||
781 | { | ||
782 | res=gmi_exec_until_addr(h,addr); | ||
783 | if (res) | ||
784 | state=running; | ||
785 | } | ||
786 | return res; | ||
787 | } | ||
788 | |||
789 | |||
790 | /**[txh]******************************************************************** | ||
791 | |||
792 | Description: | ||
793 | Resumes execution until the end of the current funtion is reached. Only | ||
794 | usable when we are in the "stopped" state. | ||
795 | |||
796 | Return: !=0 OK | ||
797 | |||
798 | ***************************************************************************/ | ||
799 | |||
800 | int MIDebugger::FinishFun() | ||
801 | { | ||
802 | if (state!=stopped) | ||
803 | return 0; | ||
804 | int res=gmi_exec_finish(h); | ||
805 | if (res) | ||
806 | state=running; | ||
807 | return res; | ||
808 | } | ||
809 | |||
810 | /**[txh]******************************************************************** | ||
811 | |||
812 | Description: | ||
813 | Returns immediately. Only usable when we are in the "stopped" state. | ||
814 | |||
815 | Return: !=NULL OK, the returned frame is the current location. That's a | ||
816 | synchronous function. | ||
817 | |||
818 | ***************************************************************************/ | ||
819 | |||
820 | mi_frames *MIDebugger::ReturnNow() | ||
821 | { | ||
822 | if (state!=stopped) | ||
823 | return 0; | ||
824 | return gmi_exec_return(h); | ||
825 | } | ||
826 | |||
827 | /**[txh]******************************************************************** | ||
828 | |||
829 | Description: | ||
830 | Returns the current list of frames. | ||
831 | |||
832 | Return: !=NULL OK, the list of frames is returned. | ||
833 | |||
834 | ***************************************************************************/ | ||
835 | |||
836 | mi_frames *MIDebugger::CallStack(bool args) | ||
837 | { | ||
838 | if (state!=stopped) | ||
839 | return 0; | ||
840 | mi_frames *fr1=gmi_stack_list_frames(h); | ||
841 | if (fr1 && args) | ||
842 | {// Get the function arguments | ||
843 | mi_frames *fr2=gmi_stack_list_arguments(h,1); | ||
844 | if (fr2) | ||
845 | {// Transfer them to the other list | ||
846 | mi_frames *p=fr1, *p2=fr2; | ||
847 | while (p2 && p) | ||
848 | { | ||
849 | p->args=p2->args; | ||
850 | p2->args=NULL; | ||
851 | p2=p2->next; | ||
852 | p=p->next; | ||
853 | } | ||
854 | mi_free_frames(fr2); | ||
855 | } | ||
856 | } | ||
857 | return fr1; | ||
858 | } | ||
859 | |||
860 | /**[txh]******************************************************************** | ||
861 | |||
862 | Description: | ||
863 | Executes upto the next line, it follows function calls. The @var{inst} | ||
864 | argument is for assembler. If the state is "target_specified" it will go to | ||
865 | the first line in the main function. If the state is "stopped" will use the | ||
866 | next command. If successfully the state will change to "running". | ||
867 | |||
868 | Return: !=0 OK | ||
869 | |||
870 | ***************************************************************************/ | ||
871 | |||
872 | int MIDebugger::TraceInto(bool inst) | ||
873 | { | ||
874 | int res=0; | ||
875 | |||
876 | if (state==target_specified) | ||
877 | {// We aren't running | ||
878 | // Walk to main | ||
879 | return RunToMain(); | ||
880 | } | ||
881 | if (state==stopped) | ||
882 | { | ||
883 | if (inst) | ||
884 | res=gmi_exec_step_instruction(h); | ||
885 | else | ||
886 | res=gmi_exec_step(h); | ||
887 | if (res) | ||
888 | state=running; | ||
889 | } | ||
890 | return res; | ||
891 | } | ||
892 | |||
893 | /**[txh]******************************************************************** | ||
894 | |||
895 | Description: | ||
896 | Evaluates the provided expression. If we get an error the error | ||
897 | description is returned instead. Can't be called if "disconnected" or | ||
898 | "running". | ||
899 | |||
900 | Return: The result of the expression (use free) or NULL. | ||
901 | |||
902 | ***************************************************************************/ | ||
903 | |||
904 | char *MIDebugger::EvalExpression(const char *exp) | ||
905 | { | ||
906 | if (state==disconnected || | ||
907 | state==running) // No async :-( | ||
908 | return NULL; | ||
909 | // Evaluate it | ||
910 | mi_error=MI_OK; | ||
911 | char *res=gmi_data_evaluate_expression(h,exp); | ||
912 | if (!res && mi_error_from_gdb) | ||
913 | {// Not valid, return the error | ||
914 | res=strdup(mi_error_from_gdb); | ||
915 | } | ||
916 | return res; | ||
917 | } | ||
918 | |||
919 | /**[txh]******************************************************************** | ||
920 | |||
921 | Description: | ||
922 | Modifies the provided expression. If we get an error the error | ||
923 | description is returned instead. Can't be called if "disconnected" or | ||
924 | "running". | ||
925 | |||
926 | Return: The result of the expression (use free) or NULL. | ||
927 | |||
928 | ***************************************************************************/ | ||
929 | |||
930 | char *MIDebugger::ModifyExpression(char *exp, char *newVal) | ||
931 | { | ||
932 | if (state==disconnected || | ||
933 | state==running) // No async :-( | ||
934 | return NULL; | ||
935 | // Create an assignment | ||
936 | int l1=strlen(exp); | ||
937 | int l2=strlen(newVal); | ||
938 | char b[l1+l2+2], *s=b; | ||
939 | memcpy(s,exp,l1); | ||
940 | s+=l1; | ||
941 | *s='='; | ||
942 | memcpy(++s,newVal,l2); | ||
943 | s[l2]=0; | ||
944 | // Evaluate it | ||
945 | char *res=gmi_data_evaluate_expression(h,b); | ||
946 | if (!res && mi_error_from_gdb) | ||
947 | {// Not valid, return the error | ||
948 | res=strdup(mi_error_from_gdb); | ||
949 | } | ||
950 | return res; | ||
951 | } | ||
952 | |||
953 | /**[txh]******************************************************************** | ||
954 | |||
955 | Description: | ||
956 | Sends a command to gdb. | ||
957 | |||
958 | Return: !=0 OK | ||
959 | |||
960 | ***************************************************************************/ | ||
961 | |||
962 | int MIDebugger::Send(const char *command) | ||
963 | { | ||
964 | if (state==disconnected || | ||
965 | state==running) // No async :-( | ||
966 | return 0; | ||
967 | // TODO: detect and use -interpreter-exec? | ||
968 | mi_send(h,"%s\n",command); | ||
969 | return mi_res_simple_done(h); | ||
970 | } | ||
971 | |||
972 | |||
973 | /**[txh]******************************************************************** | ||
974 | |||
975 | Description: | ||
976 | Fills the type and value fields of the mi_gvar provided list. | ||
977 | |||
978 | Return: !=0 OK | ||
979 | |||
980 | ***************************************************************************/ | ||
981 | |||
982 | int MIDebugger::FillTypeVal(mi_gvar *var) | ||
983 | { | ||
984 | while (var) | ||
985 | { | ||
986 | if (!var->type && !gmi_var_info_type(h,var)) | ||
987 | return 0; | ||
988 | if (!var->value && !gmi_var_evaluate_expression(h,var)) | ||
989 | return 0; | ||
990 | var=var->next; | ||
991 | } | ||
992 | return 1; | ||
993 | } | ||
994 | |||
995 | int MIDebugger::FillOneTypeVal(mi_gvar *var) | ||
996 | { | ||
997 | if (!var) | ||
998 | return 0; | ||
999 | |||
1000 | int ok=1; | ||
1001 | if (!var->type && !gmi_var_info_type(h,var)) | ||
1002 | { | ||
1003 | var->type=strdup(""); | ||
1004 | ok=0; | ||
1005 | } | ||
1006 | if (!var->value && !gmi_var_evaluate_expression(h,var)) | ||
1007 | { | ||
1008 | var->value=strdup(""); | ||
1009 | ok=0; | ||
1010 | } | ||
1011 | return ok; | ||
1012 | } | ||
1013 | |||
1014 | int MIDebugger::AssigngVar(mi_gvar *var, const char *exp) | ||
1015 | { | ||
1016 | if (state!=stopped) | ||
1017 | return 0; | ||
1018 | return gmi_var_assign(h,var,exp); | ||
1019 | } | ||
1020 | |||
1021 | char *MIDebugger::Show(const char *var) | ||
1022 | { | ||
1023 | if (state==running || state==disconnected) | ||
1024 | return 0; | ||
1025 | // GDB 5.x doesn't reply all in the response record, just to the console :-( | ||
1026 | h->catch_console=1; | ||
1027 | if (h->catched_console) | ||
1028 | { | ||
1029 | free(h->catched_console); | ||
1030 | h->catched_console=NULL; | ||
1031 | } | ||
1032 | char *res=gmi_gdb_show(h,var); | ||
1033 | h->catch_console=0; | ||
1034 | if (!res && h->catched_console) | ||
1035 | { | ||
1036 | res=h->catched_console; | ||
1037 | h->catched_console=NULL; | ||
1038 | } | ||
1039 | return res; | ||
1040 | } | ||
1041 | |||
1042 | MIDebugger::endianType MIDebugger::GetTargetEndian() | ||
1043 | { | ||
1044 | if (targetEndian!=enUnknown) | ||
1045 | return targetEndian; | ||
1046 | if (state!=stopped && state!=target_specified) | ||
1047 | return enUnknown; | ||
1048 | |||
1049 | char *end=Show("endian"); | ||
1050 | if (end) | ||
1051 | { | ||
1052 | if (strstr(end,"big")) | ||
1053 | targetEndian=enBig; | ||
1054 | else if (strstr(end,"little")) | ||
1055 | targetEndian=enLittle; | ||
1056 | free(end); | ||
1057 | } | ||
1058 | return targetEndian; | ||
1059 | } | ||
1060 | |||
1061 | MIDebugger::archType MIDebugger::GetTargetArchitecture() | ||
1062 | { | ||
1063 | if (targetArch!=arUnknown) | ||
1064 | return targetArch; | ||
1065 | if (state!=stopped && state!=target_specified) | ||
1066 | return arUnknown; | ||
1067 | |||
1068 | char *end=Show("architecture"); | ||
1069 | if (end) | ||
1070 | { | ||
1071 | if (strstr(end,"i386")) | ||
1072 | targetArch=arIA32; | ||
1073 | else if (strstr(end,"sparc")) | ||
1074 | targetArch=arSPARC; | ||
1075 | else if (strstr(end,"pic14")) | ||
1076 | targetArch=arPIC14; | ||
1077 | else if (strstr(end,"avr")) | ||
1078 | targetArch=arAVR; | ||
1079 | free(end); | ||
1080 | } | ||
1081 | return targetArch; | ||
1082 | } | ||
1083 | |||
1084 | int MIDebugger::GetErrorNumberSt() | ||
1085 | { | ||
1086 | if (mi_error==MI_GDB_DIED) | ||
1087 | { | ||
1088 | state=target_specified; | ||
1089 | TargetUnselect(); | ||
1090 | state=connected; | ||
1091 | Disconnect(); | ||
1092 | } | ||
1093 | return mi_error; | ||
1094 | } | ||
1095 | |||
1096 | int MIDebugger::UpdateRegisters(mi_chg_reg *regs) | ||
1097 | { | ||
1098 | int updated=0; | ||
1099 | mi_chg_reg *chg=GetChangedRegisters(); | ||
1100 | if (chg) | ||
1101 | { | ||
1102 | mi_chg_reg *r=regs, *c; | ||
1103 | while (r) | ||
1104 | { | ||
1105 | c=chg; | ||
1106 | while (c && c->reg!=r->reg) | ||
1107 | c=c->next; | ||
1108 | if (c) | ||
1109 | { | ||
1110 | r->updated=1; | ||
1111 | free(r->val); | ||
1112 | r->val=c->val; | ||
1113 | c->val=NULL; | ||
1114 | updated++; | ||
1115 | } | ||
1116 | else | ||
1117 | r->updated=0; | ||
1118 | r=r->next; | ||
1119 | } | ||
1120 | } | ||
1121 | return updated; | ||
1122 | } | ||
1123 | |||