GDB (API)
/home/stan/gdb/src/gdb/record-btrace.c
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines