]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gdb/record-btrace.c
Fix "is a record target open" checks.
[thirdparty/binutils-gdb.git] / gdb / record-btrace.c
1 /* Branch trace support for GDB, the GNU debugger.
2
3 Copyright (C) 2013-2014 Free Software Foundation, Inc.
4
5 Contributed by Intel Corp. <markus.t.metzger@intel.com>
6
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22 #include "defs.h"
23 #include "record.h"
24 #include "gdbthread.h"
25 #include "target.h"
26 #include "gdbcmd.h"
27 #include "disasm.h"
28 #include "observer.h"
29 #include "exceptions.h"
30 #include "cli/cli-utils.h"
31 #include "source.h"
32 #include "ui-out.h"
33 #include "symtab.h"
34 #include "filenames.h"
35
36 /* The target_ops of record-btrace. */
37 static struct target_ops record_btrace_ops;
38
39 /* A new thread observer enabling branch tracing for the new thread. */
40 static struct observer *record_btrace_thread_observer;
41
42 /* Print a record-btrace debug message. Use do ... while (0) to avoid
43 ambiguities when used in if statements. */
44
45 #define DEBUG(msg, args...) \
46 do \
47 { \
48 if (record_debug != 0) \
49 fprintf_unfiltered (gdb_stdlog, \
50 "[record-btrace] " msg "\n", ##args); \
51 } \
52 while (0)
53
54
55 /* Update the branch trace for the current thread and return a pointer to its
56 branch trace information struct.
57
58 Throws an error if there is no thread or no trace. This function never
59 returns NULL. */
60
61 static struct btrace_thread_info *
62 require_btrace (void)
63 {
64 struct thread_info *tp;
65 struct btrace_thread_info *btinfo;
66
67 DEBUG ("require");
68
69 tp = find_thread_ptid (inferior_ptid);
70 if (tp == NULL)
71 error (_("No thread."));
72
73 btrace_fetch (tp);
74
75 btinfo = &tp->btrace;
76
77 if (VEC_empty (btrace_inst_s, btinfo->itrace))
78 error (_("No trace."));
79
80 return btinfo;
81 }
82
83 /* Enable branch tracing for one thread. Warn on errors. */
84
85 static void
86 record_btrace_enable_warn (struct thread_info *tp)
87 {
88 volatile struct gdb_exception error;
89
90 TRY_CATCH (error, RETURN_MASK_ERROR)
91 btrace_enable (tp);
92
93 if (error.message != NULL)
94 warning ("%s", error.message);
95 }
96
97 /* Callback function to disable branch tracing for one thread. */
98
99 static void
100 record_btrace_disable_callback (void *arg)
101 {
102 struct thread_info *tp;
103
104 tp = arg;
105
106 btrace_disable (tp);
107 }
108
109 /* Enable automatic tracing of new threads. */
110
111 static void
112 record_btrace_auto_enable (void)
113 {
114 DEBUG ("attach thread observer");
115
116 record_btrace_thread_observer
117 = observer_attach_new_thread (record_btrace_enable_warn);
118 }
119
120 /* Disable automatic tracing of new threads. */
121
122 static void
123 record_btrace_auto_disable (void)
124 {
125 /* The observer may have been detached, already. */
126 if (record_btrace_thread_observer == NULL)
127 return;
128
129 DEBUG ("detach thread observer");
130
131 observer_detach_new_thread (record_btrace_thread_observer);
132 record_btrace_thread_observer = NULL;
133 }
134
135 /* The to_open method of target record-btrace. */
136
137 static void
138 record_btrace_open (char *args, int from_tty)
139 {
140 struct cleanup *disable_chain;
141 struct thread_info *tp;
142
143 DEBUG ("open");
144
145 record_preopen ();
146
147 if (!target_has_execution)
148 error (_("The program is not being run."));
149
150 if (!target_supports_btrace ())
151 error (_("Target does not support branch tracing."));
152
153 gdb_assert (record_btrace_thread_observer == NULL);
154
155 disable_chain = make_cleanup (null_cleanup, NULL);
156 ALL_THREADS (tp)
157 if (args == NULL || *args == 0 || number_is_in_list (args, tp->num))
158 {
159 btrace_enable (tp);
160
161 make_cleanup (record_btrace_disable_callback, tp);
162 }
163
164 record_btrace_auto_enable ();
165
166 push_target (&record_btrace_ops);
167
168 observer_notify_record_changed (current_inferior (), 1);
169
170 discard_cleanups (disable_chain);
171 }
172
173 /* The to_stop_recording method of target record-btrace. */
174
175 static void
176 record_btrace_stop_recording (void)
177 {
178 struct thread_info *tp;
179
180 DEBUG ("stop recording");
181
182 record_btrace_auto_disable ();
183
184 ALL_THREADS (tp)
185 if (tp->btrace.target != NULL)
186 btrace_disable (tp);
187 }
188
189 /* The to_close method of target record-btrace. */
190
191 static void
192 record_btrace_close (void)
193 {
194 /* Make sure automatic recording gets disabled even if we did not stop
195 recording before closing the record-btrace target. */
196 record_btrace_auto_disable ();
197
198 /* We already stopped recording. */
199 }
200
201 /* The to_info_record method of target record-btrace. */
202
203 static void
204 record_btrace_info (void)
205 {
206 struct btrace_thread_info *btinfo;
207 struct thread_info *tp;
208 unsigned int insts, funcs;
209
210 DEBUG ("info");
211
212 tp = find_thread_ptid (inferior_ptid);
213 if (tp == NULL)
214 error (_("No thread."));
215
216 btrace_fetch (tp);
217
218 btinfo = &tp->btrace;
219 insts = VEC_length (btrace_inst_s, btinfo->itrace);
220 funcs = VEC_length (btrace_func_s, btinfo->ftrace);
221
222 printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
223 "%d (%s).\n"), insts, funcs, tp->num,
224 target_pid_to_str (tp->ptid));
225 }
226
227 /* Print an unsigned int. */
228
229 static void
230 ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
231 {
232 ui_out_field_fmt (uiout, fld, "%u", val);
233 }
234
235 /* Disassemble a section of the recorded instruction trace. */
236
237 static void
238 btrace_insn_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
239 unsigned int begin, unsigned int end, int flags)
240 {
241 struct gdbarch *gdbarch;
242 struct btrace_inst *inst;
243 unsigned int idx;
244
245 DEBUG ("itrace (0x%x): [%u; %u[", flags, begin, end);
246
247 gdbarch = target_gdbarch ();
248
249 for (idx = begin; VEC_iterate (btrace_inst_s, btinfo->itrace, idx, inst)
250 && idx < end; ++idx)
251 {
252 /* Print the instruction index. */
253 ui_out_field_uint (uiout, "index", idx);
254 ui_out_text (uiout, "\t");
255
256 /* Disassembly with '/m' flag may not produce the expected result.
257 See PR gdb/11833. */
258 gdb_disassembly (gdbarch, uiout, NULL, flags, 1, inst->pc, inst->pc + 1);
259 }
260 }
261
262 /* The to_insn_history method of target record-btrace. */
263
264 static void
265 record_btrace_insn_history (int size, int flags)
266 {
267 struct btrace_thread_info *btinfo;
268 struct cleanup *uiout_cleanup;
269 struct ui_out *uiout;
270 unsigned int context, last, begin, end;
271
272 uiout = current_uiout;
273 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
274 "insn history");
275 btinfo = require_btrace ();
276 last = VEC_length (btrace_inst_s, btinfo->itrace);
277
278 context = abs (size);
279 begin = btinfo->insn_iterator.begin;
280 end = btinfo->insn_iterator.end;
281
282 DEBUG ("insn-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
283
284 if (context == 0)
285 error (_("Bad record instruction-history-size."));
286
287 /* We start at the end. */
288 if (end < begin)
289 {
290 /* Truncate the context, if necessary. */
291 context = min (context, last);
292
293 end = last;
294 begin = end - context;
295 }
296 else if (size < 0)
297 {
298 if (begin == 0)
299 {
300 printf_unfiltered (_("At the start of the branch trace record.\n"));
301
302 btinfo->insn_iterator.end = 0;
303 return;
304 }
305
306 /* Truncate the context, if necessary. */
307 context = min (context, begin);
308
309 end = begin;
310 begin -= context;
311 }
312 else
313 {
314 if (end == last)
315 {
316 printf_unfiltered (_("At the end of the branch trace record.\n"));
317
318 btinfo->insn_iterator.begin = last;
319 return;
320 }
321
322 /* Truncate the context, if necessary. */
323 context = min (context, last - end);
324
325 begin = end;
326 end += context;
327 }
328
329 btrace_insn_history (btinfo, uiout, begin, end, flags);
330
331 btinfo->insn_iterator.begin = begin;
332 btinfo->insn_iterator.end = end;
333
334 do_cleanups (uiout_cleanup);
335 }
336
337 /* The to_insn_history_range method of target record-btrace. */
338
339 static void
340 record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags)
341 {
342 struct btrace_thread_info *btinfo;
343 struct cleanup *uiout_cleanup;
344 struct ui_out *uiout;
345 unsigned int last, begin, end;
346
347 uiout = current_uiout;
348 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
349 "insn history");
350 btinfo = require_btrace ();
351 last = VEC_length (btrace_inst_s, btinfo->itrace);
352
353 begin = (unsigned int) from;
354 end = (unsigned int) to;
355
356 DEBUG ("insn-history (0x%x): [%u; %u[", flags, begin, end);
357
358 /* Check for wrap-arounds. */
359 if (begin != from || end != to)
360 error (_("Bad range."));
361
362 if (end <= begin)
363 error (_("Bad range."));
364
365 if (last <= begin)
366 error (_("Range out of bounds."));
367
368 /* Truncate the range, if necessary. */
369 if (last < end)
370 end = last;
371
372 btrace_insn_history (btinfo, uiout, begin, end, flags);
373
374 btinfo->insn_iterator.begin = begin;
375 btinfo->insn_iterator.end = end;
376
377 do_cleanups (uiout_cleanup);
378 }
379
380 /* The to_insn_history_from method of target record-btrace. */
381
382 static void
383 record_btrace_insn_history_from (ULONGEST from, int size, int flags)
384 {
385 ULONGEST begin, end, context;
386
387 context = abs (size);
388
389 if (size < 0)
390 {
391 end = from;
392
393 if (from < context)
394 begin = 0;
395 else
396 begin = from - context;
397 }
398 else
399 {
400 begin = from;
401 end = from + context;
402
403 /* Check for wrap-around. */
404 if (end < begin)
405 end = ULONGEST_MAX;
406 }
407
408 record_btrace_insn_history_range (begin, end, flags);
409 }
410
411 /* Print the instruction number range for a function call history line. */
412
413 static void
414 btrace_func_history_insn_range (struct ui_out *uiout, struct btrace_func *bfun)
415 {
416 ui_out_field_uint (uiout, "insn begin", bfun->ibegin);
417
418 if (bfun->ibegin == bfun->iend)
419 return;
420
421 ui_out_text (uiout, "-");
422 ui_out_field_uint (uiout, "insn end", bfun->iend);
423 }
424
425 /* Print the source line information for a function call history line. */
426
427 static void
428 btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun)
429 {
430 struct symbol *sym;
431
432 sym = bfun->sym;
433 if (sym == NULL)
434 return;
435
436 ui_out_field_string (uiout, "file",
437 symtab_to_filename_for_display (sym->symtab));
438
439 if (bfun->lend == 0)
440 return;
441
442 ui_out_text (uiout, ":");
443 ui_out_field_int (uiout, "min line", bfun->lbegin);
444
445 if (bfun->lend == bfun->lbegin)
446 return;
447
448 ui_out_text (uiout, "-");
449 ui_out_field_int (uiout, "max line", bfun->lend);
450 }
451
452 /* Disassemble a section of the recorded function trace. */
453
454 static void
455 btrace_func_history (struct btrace_thread_info *btinfo, struct ui_out *uiout,
456 unsigned int begin, unsigned int end,
457 enum record_print_flag flags)
458 {
459 struct btrace_func *bfun;
460 unsigned int idx;
461
462 DEBUG ("ftrace (0x%x): [%u; %u[", flags, begin, end);
463
464 for (idx = begin; VEC_iterate (btrace_func_s, btinfo->ftrace, idx, bfun)
465 && idx < end; ++idx)
466 {
467 /* Print the function index. */
468 ui_out_field_uint (uiout, "index", idx);
469 ui_out_text (uiout, "\t");
470
471 if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
472 {
473 btrace_func_history_insn_range (uiout, bfun);
474 ui_out_text (uiout, "\t");
475 }
476
477 if ((flags & RECORD_PRINT_SRC_LINE) != 0)
478 {
479 btrace_func_history_src_line (uiout, bfun);
480 ui_out_text (uiout, "\t");
481 }
482
483 if (bfun->sym != NULL)
484 ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->sym));
485 else if (bfun->msym != NULL)
486 ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->msym));
487 ui_out_text (uiout, "\n");
488 }
489 }
490
491 /* The to_call_history method of target record-btrace. */
492
493 static void
494 record_btrace_call_history (int size, int flags)
495 {
496 struct btrace_thread_info *btinfo;
497 struct cleanup *uiout_cleanup;
498 struct ui_out *uiout;
499 unsigned int context, last, begin, end;
500
501 uiout = current_uiout;
502 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
503 "insn history");
504 btinfo = require_btrace ();
505 last = VEC_length (btrace_func_s, btinfo->ftrace);
506
507 context = abs (size);
508 begin = btinfo->func_iterator.begin;
509 end = btinfo->func_iterator.end;
510
511 DEBUG ("func-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end);
512
513 if (context == 0)
514 error (_("Bad record function-call-history-size."));
515
516 /* We start at the end. */
517 if (end < begin)
518 {
519 /* Truncate the context, if necessary. */
520 context = min (context, last);
521
522 end = last;
523 begin = end - context;
524 }
525 else if (size < 0)
526 {
527 if (begin == 0)
528 {
529 printf_unfiltered (_("At the start of the branch trace record.\n"));
530
531 btinfo->func_iterator.end = 0;
532 return;
533 }
534
535 /* Truncate the context, if necessary. */
536 context = min (context, begin);
537
538 end = begin;
539 begin -= context;
540 }
541 else
542 {
543 if (end == last)
544 {
545 printf_unfiltered (_("At the end of the branch trace record.\n"));
546
547 btinfo->func_iterator.begin = last;
548 return;
549 }
550
551 /* Truncate the context, if necessary. */
552 context = min (context, last - end);
553
554 begin = end;
555 end += context;
556 }
557
558 btrace_func_history (btinfo, uiout, begin, end, flags);
559
560 btinfo->func_iterator.begin = begin;
561 btinfo->func_iterator.end = end;
562
563 do_cleanups (uiout_cleanup);
564 }
565
566 /* The to_call_history_range method of target record-btrace. */
567
568 static void
569 record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags)
570 {
571 struct btrace_thread_info *btinfo;
572 struct cleanup *uiout_cleanup;
573 struct ui_out *uiout;
574 unsigned int last, begin, end;
575
576 uiout = current_uiout;
577 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout,
578 "func history");
579 btinfo = require_btrace ();
580 last = VEC_length (btrace_func_s, btinfo->ftrace);
581
582 begin = (unsigned int) from;
583 end = (unsigned int) to;
584
585 DEBUG ("func-history (0x%x): [%u; %u[", flags, begin, end);
586
587 /* Check for wrap-arounds. */
588 if (begin != from || end != to)
589 error (_("Bad range."));
590
591 if (end <= begin)
592 error (_("Bad range."));
593
594 if (last <= begin)
595 error (_("Range out of bounds."));
596
597 /* Truncate the range, if necessary. */
598 if (last < end)
599 end = last;
600
601 btrace_func_history (btinfo, uiout, begin, end, flags);
602
603 btinfo->func_iterator.begin = begin;
604 btinfo->func_iterator.end = end;
605
606 do_cleanups (uiout_cleanup);
607 }
608
609 /* The to_call_history_from method of target record-btrace. */
610
611 static void
612 record_btrace_call_history_from (ULONGEST from, int size, int flags)
613 {
614 ULONGEST begin, end, context;
615
616 context = abs (size);
617
618 if (size < 0)
619 {
620 end = from;
621
622 if (from < context)
623 begin = 0;
624 else
625 begin = from - context;
626 }
627 else
628 {
629 begin = from;
630 end = from + context;
631
632 /* Check for wrap-around. */
633 if (end < begin)
634 end = ULONGEST_MAX;
635 }
636
637 record_btrace_call_history_range (begin, end, flags);
638 }
639
640 /* Initialize the record-btrace target ops. */
641
642 static void
643 init_record_btrace_ops (void)
644 {
645 struct target_ops *ops;
646
647 ops = &record_btrace_ops;
648 ops->to_shortname = "record-btrace";
649 ops->to_longname = "Branch tracing target";
650 ops->to_doc = "Collect control-flow trace and provide the execution history.";
651 ops->to_open = record_btrace_open;
652 ops->to_close = record_btrace_close;
653 ops->to_detach = record_detach;
654 ops->to_disconnect = record_disconnect;
655 ops->to_mourn_inferior = record_mourn_inferior;
656 ops->to_kill = record_kill;
657 ops->to_create_inferior = find_default_create_inferior;
658 ops->to_stop_recording = record_btrace_stop_recording;
659 ops->to_info_record = record_btrace_info;
660 ops->to_insn_history = record_btrace_insn_history;
661 ops->to_insn_history_from = record_btrace_insn_history_from;
662 ops->to_insn_history_range = record_btrace_insn_history_range;
663 ops->to_call_history = record_btrace_call_history;
664 ops->to_call_history_from = record_btrace_call_history_from;
665 ops->to_call_history_range = record_btrace_call_history_range;
666 ops->to_stratum = record_stratum;
667 ops->to_magic = OPS_MAGIC;
668 }
669
670 /* Alias for "target record". */
671
672 static void
673 cmd_record_btrace_start (char *args, int from_tty)
674 {
675 if (args != NULL && *args != 0)
676 error (_("Invalid argument."));
677
678 execute_command ("target record-btrace", from_tty);
679 }
680
681 void _initialize_record_btrace (void);
682
683 /* Initialize btrace commands. */
684
685 void
686 _initialize_record_btrace (void)
687 {
688 add_cmd ("btrace", class_obscure, cmd_record_btrace_start,
689 _("Start branch trace recording."),
690 &record_cmdlist);
691 add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist);
692
693 init_record_btrace_ops ();
694 add_target (&record_btrace_ops);
695 }