GDB (API)
|
00001 /* Branch trace support for GDB, the GNU debugger. 00002 00003 Copyright (C) 2013 Free Software Foundation, Inc. 00004 00005 Contributed by Intel Corp. <markus.t.metzger@intel.com> 00006 00007 This file is part of GDB. 00008 00009 This program is free software; you can redistribute it and/or modify 00010 it under the terms of the GNU General Public License as published by 00011 the Free Software Foundation; either version 3 of the License, or 00012 (at your option) any later version. 00013 00014 This program is distributed in the hope that it will be useful, 00015 but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 GNU General Public License for more details. 00018 00019 You should have received a copy of the GNU General Public License 00020 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 00021 00022 #include "defs.h" 00023 #include "record.h" 00024 #include "gdbthread.h" 00025 #include "target.h" 00026 #include "gdbcmd.h" 00027 #include "disasm.h" 00028 #include "observer.h" 00029 #include "exceptions.h" 00030 #include "cli/cli-utils.h" 00031 #include "source.h" 00032 #include "ui-out.h" 00033 #include "symtab.h" 00034 #include "filenames.h" 00035 00036 /* The target_ops of record-btrace. */ 00037 static struct target_ops record_btrace_ops; 00038 00039 /* A new thread observer enabling branch tracing for the new thread. */ 00040 static struct observer *record_btrace_thread_observer; 00041 00042 /* Print a record-btrace debug message. Use do ... while (0) to avoid 00043 ambiguities when used in if statements. */ 00044 00045 #define DEBUG(msg, args...) \ 00046 do \ 00047 { \ 00048 if (record_debug != 0) \ 00049 fprintf_unfiltered (gdb_stdlog, \ 00050 "[record-btrace] " msg "\n", ##args); \ 00051 } \ 00052 while (0) 00053 00054 00055 /* Update the branch trace for the current thread and return a pointer to its 00056 branch trace information struct. 00057 00058 Throws an error if there is no thread or no trace. This function never 00059 returns NULL. */ 00060 00061 static struct btrace_thread_info * 00062 require_btrace (void) 00063 { 00064 struct thread_info *tp; 00065 struct btrace_thread_info *btinfo; 00066 00067 DEBUG ("require"); 00068 00069 tp = find_thread_ptid (inferior_ptid); 00070 if (tp == NULL) 00071 error (_("No thread.")); 00072 00073 btrace_fetch (tp); 00074 00075 btinfo = &tp->btrace; 00076 00077 if (VEC_empty (btrace_inst_s, btinfo->itrace)) 00078 error (_("No trace.")); 00079 00080 return btinfo; 00081 } 00082 00083 /* Enable branch tracing for one thread. Warn on errors. */ 00084 00085 static void 00086 record_btrace_enable_warn (struct thread_info *tp) 00087 { 00088 volatile struct gdb_exception error; 00089 00090 TRY_CATCH (error, RETURN_MASK_ERROR) 00091 btrace_enable (tp); 00092 00093 if (error.message != NULL) 00094 warning ("%s", error.message); 00095 } 00096 00097 /* Callback function to disable branch tracing for one thread. */ 00098 00099 static void 00100 record_btrace_disable_callback (void *arg) 00101 { 00102 struct thread_info *tp; 00103 00104 tp = arg; 00105 00106 btrace_disable (tp); 00107 } 00108 00109 /* Enable automatic tracing of new threads. */ 00110 00111 static void 00112 record_btrace_auto_enable (void) 00113 { 00114 DEBUG ("attach thread observer"); 00115 00116 record_btrace_thread_observer 00117 = observer_attach_new_thread (record_btrace_enable_warn); 00118 } 00119 00120 /* Disable automatic tracing of new threads. */ 00121 00122 static void 00123 record_btrace_auto_disable (void) 00124 { 00125 /* The observer may have been detached, already. */ 00126 if (record_btrace_thread_observer == NULL) 00127 return; 00128 00129 DEBUG ("detach thread observer"); 00130 00131 observer_detach_new_thread (record_btrace_thread_observer); 00132 record_btrace_thread_observer = NULL; 00133 } 00134 00135 /* The to_open method of target record-btrace. */ 00136 00137 static void 00138 record_btrace_open (char *args, int from_tty) 00139 { 00140 struct cleanup *disable_chain; 00141 struct thread_info *tp; 00142 00143 DEBUG ("open"); 00144 00145 if (RECORD_IS_USED) 00146 error (_("The process is already being recorded.")); 00147 00148 if (!target_has_execution) 00149 error (_("The program is not being run.")); 00150 00151 if (!target_supports_btrace ()) 00152 error (_("Target does not support branch tracing.")); 00153 00154 gdb_assert (record_btrace_thread_observer == NULL); 00155 00156 disable_chain = make_cleanup (null_cleanup, NULL); 00157 ALL_THREADS (tp) 00158 if (args == NULL || *args == 0 || number_is_in_list (args, tp->num)) 00159 { 00160 btrace_enable (tp); 00161 00162 make_cleanup (record_btrace_disable_callback, tp); 00163 } 00164 00165 record_btrace_auto_enable (); 00166 00167 push_target (&record_btrace_ops); 00168 00169 observer_notify_record_changed (current_inferior (), 1); 00170 00171 discard_cleanups (disable_chain); 00172 } 00173 00174 /* The to_stop_recording method of target record-btrace. */ 00175 00176 static void 00177 record_btrace_stop_recording (void) 00178 { 00179 struct thread_info *tp; 00180 00181 DEBUG ("stop recording"); 00182 00183 record_btrace_auto_disable (); 00184 00185 ALL_THREADS (tp) 00186 if (tp->btrace.target != NULL) 00187 btrace_disable (tp); 00188 } 00189 00190 /* The to_close method of target record-btrace. */ 00191 00192 static void 00193 record_btrace_close (void) 00194 { 00195 /* Make sure automatic recording gets disabled even if we did not stop 00196 recording before closing the record-btrace target. */ 00197 record_btrace_auto_disable (); 00198 00199 /* We already stopped recording. */ 00200 } 00201 00202 /* The to_info_record method of target record-btrace. */ 00203 00204 static void 00205 record_btrace_info (void) 00206 { 00207 struct btrace_thread_info *btinfo; 00208 struct thread_info *tp; 00209 unsigned int insts, funcs; 00210 00211 DEBUG ("info"); 00212 00213 tp = find_thread_ptid (inferior_ptid); 00214 if (tp == NULL) 00215 error (_("No thread.")); 00216 00217 btrace_fetch (tp); 00218 00219 btinfo = &tp->btrace; 00220 insts = VEC_length (btrace_inst_s, btinfo->itrace); 00221 funcs = VEC_length (btrace_func_s, btinfo->ftrace); 00222 00223 printf_unfiltered (_("Recorded %u instructions in %u functions for thread " 00224 "%d (%s).\n"), insts, funcs, tp->num, 00225 target_pid_to_str (tp->ptid)); 00226 } 00227 00228 /* Print an unsigned int. */ 00229 00230 static void 00231 ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val) 00232 { 00233 ui_out_field_fmt (uiout, fld, "%u", val); 00234 } 00235 00236 /* Disassemble a section of the recorded instruction trace. */ 00237 00238 static void 00239 btrace_insn_history (struct btrace_thread_info *btinfo, struct ui_out *uiout, 00240 unsigned int begin, unsigned int end, int flags) 00241 { 00242 struct gdbarch *gdbarch; 00243 struct btrace_inst *inst; 00244 unsigned int idx; 00245 00246 DEBUG ("itrace (0x%x): [%u; %u[", flags, begin, end); 00247 00248 gdbarch = target_gdbarch (); 00249 00250 for (idx = begin; VEC_iterate (btrace_inst_s, btinfo->itrace, idx, inst) 00251 && idx < end; ++idx) 00252 { 00253 /* Print the instruction index. */ 00254 ui_out_field_uint (uiout, "index", idx); 00255 ui_out_text (uiout, "\t"); 00256 00257 /* Disassembly with '/m' flag may not produce the expected result. 00258 See PR gdb/11833. */ 00259 gdb_disassembly (gdbarch, uiout, NULL, flags, 1, inst->pc, inst->pc + 1); 00260 } 00261 } 00262 00263 /* The to_insn_history method of target record-btrace. */ 00264 00265 static void 00266 record_btrace_insn_history (int size, int flags) 00267 { 00268 struct btrace_thread_info *btinfo; 00269 struct cleanup *uiout_cleanup; 00270 struct ui_out *uiout; 00271 unsigned int context, last, begin, end; 00272 00273 uiout = current_uiout; 00274 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, 00275 "insn history"); 00276 btinfo = require_btrace (); 00277 last = VEC_length (btrace_inst_s, btinfo->itrace); 00278 00279 context = abs (size); 00280 begin = btinfo->insn_iterator.begin; 00281 end = btinfo->insn_iterator.end; 00282 00283 DEBUG ("insn-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end); 00284 00285 if (context == 0) 00286 error (_("Bad record instruction-history-size.")); 00287 00288 /* We start at the end. */ 00289 if (end < begin) 00290 { 00291 /* Truncate the context, if necessary. */ 00292 context = min (context, last); 00293 00294 end = last; 00295 begin = end - context; 00296 } 00297 else if (size < 0) 00298 { 00299 if (begin == 0) 00300 { 00301 printf_unfiltered (_("At the start of the branch trace record.\n")); 00302 00303 btinfo->insn_iterator.end = 0; 00304 return; 00305 } 00306 00307 /* Truncate the context, if necessary. */ 00308 context = min (context, begin); 00309 00310 end = begin; 00311 begin -= context; 00312 } 00313 else 00314 { 00315 if (end == last) 00316 { 00317 printf_unfiltered (_("At the end of the branch trace record.\n")); 00318 00319 btinfo->insn_iterator.begin = last; 00320 return; 00321 } 00322 00323 /* Truncate the context, if necessary. */ 00324 context = min (context, last - end); 00325 00326 begin = end; 00327 end += context; 00328 } 00329 00330 btrace_insn_history (btinfo, uiout, begin, end, flags); 00331 00332 btinfo->insn_iterator.begin = begin; 00333 btinfo->insn_iterator.end = end; 00334 00335 do_cleanups (uiout_cleanup); 00336 } 00337 00338 /* The to_insn_history_range method of target record-btrace. */ 00339 00340 static void 00341 record_btrace_insn_history_range (ULONGEST from, ULONGEST to, int flags) 00342 { 00343 struct btrace_thread_info *btinfo; 00344 struct cleanup *uiout_cleanup; 00345 struct ui_out *uiout; 00346 unsigned int last, begin, end; 00347 00348 uiout = current_uiout; 00349 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, 00350 "insn history"); 00351 btinfo = require_btrace (); 00352 last = VEC_length (btrace_inst_s, btinfo->itrace); 00353 00354 begin = (unsigned int) from; 00355 end = (unsigned int) to; 00356 00357 DEBUG ("insn-history (0x%x): [%u; %u[", flags, begin, end); 00358 00359 /* Check for wrap-arounds. */ 00360 if (begin != from || end != to) 00361 error (_("Bad range.")); 00362 00363 if (end <= begin) 00364 error (_("Bad range.")); 00365 00366 if (last <= begin) 00367 error (_("Range out of bounds.")); 00368 00369 /* Truncate the range, if necessary. */ 00370 if (last < end) 00371 end = last; 00372 00373 btrace_insn_history (btinfo, uiout, begin, end, flags); 00374 00375 btinfo->insn_iterator.begin = begin; 00376 btinfo->insn_iterator.end = end; 00377 00378 do_cleanups (uiout_cleanup); 00379 } 00380 00381 /* The to_insn_history_from method of target record-btrace. */ 00382 00383 static void 00384 record_btrace_insn_history_from (ULONGEST from, int size, int flags) 00385 { 00386 ULONGEST begin, end, context; 00387 00388 context = abs (size); 00389 00390 if (size < 0) 00391 { 00392 end = from; 00393 00394 if (from < context) 00395 begin = 0; 00396 else 00397 begin = from - context; 00398 } 00399 else 00400 { 00401 begin = from; 00402 end = from + context; 00403 00404 /* Check for wrap-around. */ 00405 if (end < begin) 00406 end = ULONGEST_MAX; 00407 } 00408 00409 record_btrace_insn_history_range (begin, end, flags); 00410 } 00411 00412 /* Print the instruction number range for a function call history line. */ 00413 00414 static void 00415 btrace_func_history_insn_range (struct ui_out *uiout, struct btrace_func *bfun) 00416 { 00417 ui_out_field_uint (uiout, "insn begin", bfun->ibegin); 00418 00419 if (bfun->ibegin == bfun->iend) 00420 return; 00421 00422 ui_out_text (uiout, "-"); 00423 ui_out_field_uint (uiout, "insn end", bfun->iend); 00424 } 00425 00426 /* Print the source line information for a function call history line. */ 00427 00428 static void 00429 btrace_func_history_src_line (struct ui_out *uiout, struct btrace_func *bfun) 00430 { 00431 struct symbol *sym; 00432 00433 sym = bfun->sym; 00434 if (sym == NULL) 00435 return; 00436 00437 ui_out_field_string (uiout, "file", 00438 symtab_to_filename_for_display (sym->symtab)); 00439 00440 if (bfun->lend == 0) 00441 return; 00442 00443 ui_out_text (uiout, ":"); 00444 ui_out_field_int (uiout, "min line", bfun->lbegin); 00445 00446 if (bfun->lend == bfun->lbegin) 00447 return; 00448 00449 ui_out_text (uiout, "-"); 00450 ui_out_field_int (uiout, "max line", bfun->lend); 00451 } 00452 00453 /* Disassemble a section of the recorded function trace. */ 00454 00455 static void 00456 btrace_func_history (struct btrace_thread_info *btinfo, struct ui_out *uiout, 00457 unsigned int begin, unsigned int end, 00458 enum record_print_flag flags) 00459 { 00460 struct btrace_func *bfun; 00461 unsigned int idx; 00462 00463 DEBUG ("ftrace (0x%x): [%u; %u[", flags, begin, end); 00464 00465 for (idx = begin; VEC_iterate (btrace_func_s, btinfo->ftrace, idx, bfun) 00466 && idx < end; ++idx) 00467 { 00468 /* Print the function index. */ 00469 ui_out_field_uint (uiout, "index", idx); 00470 ui_out_text (uiout, "\t"); 00471 00472 if ((flags & RECORD_PRINT_INSN_RANGE) != 0) 00473 { 00474 btrace_func_history_insn_range (uiout, bfun); 00475 ui_out_text (uiout, "\t"); 00476 } 00477 00478 if ((flags & RECORD_PRINT_SRC_LINE) != 0) 00479 { 00480 btrace_func_history_src_line (uiout, bfun); 00481 ui_out_text (uiout, "\t"); 00482 } 00483 00484 if (bfun->sym != NULL) 00485 ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->sym)); 00486 else if (bfun->msym != NULL) 00487 ui_out_field_string (uiout, "function", SYMBOL_PRINT_NAME (bfun->msym)); 00488 ui_out_text (uiout, "\n"); 00489 } 00490 } 00491 00492 /* The to_call_history method of target record-btrace. */ 00493 00494 static void 00495 record_btrace_call_history (int size, int flags) 00496 { 00497 struct btrace_thread_info *btinfo; 00498 struct cleanup *uiout_cleanup; 00499 struct ui_out *uiout; 00500 unsigned int context, last, begin, end; 00501 00502 uiout = current_uiout; 00503 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, 00504 "insn history"); 00505 btinfo = require_btrace (); 00506 last = VEC_length (btrace_func_s, btinfo->ftrace); 00507 00508 context = abs (size); 00509 begin = btinfo->func_iterator.begin; 00510 end = btinfo->func_iterator.end; 00511 00512 DEBUG ("func-history (0x%x): %d, prev: [%u; %u[", flags, size, begin, end); 00513 00514 if (context == 0) 00515 error (_("Bad record function-call-history-size.")); 00516 00517 /* We start at the end. */ 00518 if (end < begin) 00519 { 00520 /* Truncate the context, if necessary. */ 00521 context = min (context, last); 00522 00523 end = last; 00524 begin = end - context; 00525 } 00526 else if (size < 0) 00527 { 00528 if (begin == 0) 00529 { 00530 printf_unfiltered (_("At the start of the branch trace record.\n")); 00531 00532 btinfo->func_iterator.end = 0; 00533 return; 00534 } 00535 00536 /* Truncate the context, if necessary. */ 00537 context = min (context, begin); 00538 00539 end = begin; 00540 begin -= context; 00541 } 00542 else 00543 { 00544 if (end == last) 00545 { 00546 printf_unfiltered (_("At the end of the branch trace record.\n")); 00547 00548 btinfo->func_iterator.begin = last; 00549 return; 00550 } 00551 00552 /* Truncate the context, if necessary. */ 00553 context = min (context, last - end); 00554 00555 begin = end; 00556 end += context; 00557 } 00558 00559 btrace_func_history (btinfo, uiout, begin, end, flags); 00560 00561 btinfo->func_iterator.begin = begin; 00562 btinfo->func_iterator.end = end; 00563 00564 do_cleanups (uiout_cleanup); 00565 } 00566 00567 /* The to_call_history_range method of target record-btrace. */ 00568 00569 static void 00570 record_btrace_call_history_range (ULONGEST from, ULONGEST to, int flags) 00571 { 00572 struct btrace_thread_info *btinfo; 00573 struct cleanup *uiout_cleanup; 00574 struct ui_out *uiout; 00575 unsigned int last, begin, end; 00576 00577 uiout = current_uiout; 00578 uiout_cleanup = make_cleanup_ui_out_tuple_begin_end (uiout, 00579 "func history"); 00580 btinfo = require_btrace (); 00581 last = VEC_length (btrace_func_s, btinfo->ftrace); 00582 00583 begin = (unsigned int) from; 00584 end = (unsigned int) to; 00585 00586 DEBUG ("func-history (0x%x): [%u; %u[", flags, begin, end); 00587 00588 /* Check for wrap-arounds. */ 00589 if (begin != from || end != to) 00590 error (_("Bad range.")); 00591 00592 if (end <= begin) 00593 error (_("Bad range.")); 00594 00595 if (last <= begin) 00596 error (_("Range out of bounds.")); 00597 00598 /* Truncate the range, if necessary. */ 00599 if (last < end) 00600 end = last; 00601 00602 btrace_func_history (btinfo, uiout, begin, end, flags); 00603 00604 btinfo->func_iterator.begin = begin; 00605 btinfo->func_iterator.end = end; 00606 00607 do_cleanups (uiout_cleanup); 00608 } 00609 00610 /* The to_call_history_from method of target record-btrace. */ 00611 00612 static void 00613 record_btrace_call_history_from (ULONGEST from, int size, int flags) 00614 { 00615 ULONGEST begin, end, context; 00616 00617 context = abs (size); 00618 00619 if (size < 0) 00620 { 00621 end = from; 00622 00623 if (from < context) 00624 begin = 0; 00625 else 00626 begin = from - context; 00627 } 00628 else 00629 { 00630 begin = from; 00631 end = from + context; 00632 00633 /* Check for wrap-around. */ 00634 if (end < begin) 00635 end = ULONGEST_MAX; 00636 } 00637 00638 record_btrace_call_history_range (begin, end, flags); 00639 } 00640 00641 /* Initialize the record-btrace target ops. */ 00642 00643 static void 00644 init_record_btrace_ops (void) 00645 { 00646 struct target_ops *ops; 00647 00648 ops = &record_btrace_ops; 00649 ops->to_shortname = "record-btrace"; 00650 ops->to_longname = "Branch tracing target"; 00651 ops->to_doc = "Collect control-flow trace and provide the execution history."; 00652 ops->to_open = record_btrace_open; 00653 ops->to_close = record_btrace_close; 00654 ops->to_detach = record_detach; 00655 ops->to_disconnect = record_disconnect; 00656 ops->to_mourn_inferior = record_mourn_inferior; 00657 ops->to_kill = record_kill; 00658 ops->to_create_inferior = find_default_create_inferior; 00659 ops->to_stop_recording = record_btrace_stop_recording; 00660 ops->to_info_record = record_btrace_info; 00661 ops->to_insn_history = record_btrace_insn_history; 00662 ops->to_insn_history_from = record_btrace_insn_history_from; 00663 ops->to_insn_history_range = record_btrace_insn_history_range; 00664 ops->to_call_history = record_btrace_call_history; 00665 ops->to_call_history_from = record_btrace_call_history_from; 00666 ops->to_call_history_range = record_btrace_call_history_range; 00667 ops->to_stratum = record_stratum; 00668 ops->to_magic = OPS_MAGIC; 00669 } 00670 00671 /* Alias for "target record". */ 00672 00673 static void 00674 cmd_record_btrace_start (char *args, int from_tty) 00675 { 00676 if (args != NULL && *args != 0) 00677 error (_("Invalid argument.")); 00678 00679 execute_command ("target record-btrace", from_tty); 00680 } 00681 00682 void _initialize_record_btrace (void); 00683 00684 /* Initialize btrace commands. */ 00685 00686 void 00687 _initialize_record_btrace (void) 00688 { 00689 add_cmd ("btrace", class_obscure, cmd_record_btrace_start, 00690 _("Start branch trace recording."), 00691 &record_cmdlist); 00692 add_alias_cmd ("b", "btrace", class_obscure, 1, &record_cmdlist); 00693 00694 init_record_btrace_ops (); 00695 add_target (&record_btrace_ops); 00696 }