GDB (API)
/home/stan/gdb/src/gdb/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 "btrace.h"
00023 #include "gdbthread.h"
00024 #include "exceptions.h"
00025 #include "inferior.h"
00026 #include "target.h"
00027 #include "record.h"
00028 #include "symtab.h"
00029 #include "disasm.h"
00030 #include "source.h"
00031 #include "filenames.h"
00032 #include "xml-support.h"
00033 
00034 /* Print a record debug message.  Use do ... while (0) to avoid ambiguities
00035    when used in if statements.  */
00036 
00037 #define DEBUG(msg, args...)                                             \
00038   do                                                                    \
00039     {                                                                   \
00040       if (record_debug != 0)                                            \
00041         fprintf_unfiltered (gdb_stdlog,                                 \
00042                             "[btrace] " msg "\n", ##args);              \
00043     }                                                                   \
00044   while (0)
00045 
00046 #define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
00047 
00048 /* Initialize the instruction iterator.  */
00049 
00050 static void
00051 btrace_init_insn_iterator (struct btrace_thread_info *btinfo)
00052 {
00053   DEBUG ("init insn iterator");
00054 
00055   btinfo->insn_iterator.begin = 1;
00056   btinfo->insn_iterator.end = 0;
00057 }
00058 
00059 /* Initialize the function iterator.  */
00060 
00061 static void
00062 btrace_init_func_iterator (struct btrace_thread_info *btinfo)
00063 {
00064   DEBUG ("init func iterator");
00065 
00066   btinfo->func_iterator.begin = 1;
00067   btinfo->func_iterator.end = 0;
00068 }
00069 
00070 /* Compute the instruction trace from the block trace.  */
00071 
00072 static VEC (btrace_inst_s) *
00073 compute_itrace (VEC (btrace_block_s) *btrace)
00074 {
00075   VEC (btrace_inst_s) *itrace;
00076   struct gdbarch *gdbarch;
00077   unsigned int b;
00078 
00079   DEBUG ("compute itrace");
00080 
00081   itrace = NULL;
00082   gdbarch = target_gdbarch ();
00083   b = VEC_length (btrace_block_s, btrace);
00084 
00085   while (b-- != 0)
00086     {
00087       btrace_block_s *block;
00088       CORE_ADDR pc;
00089 
00090       block = VEC_index (btrace_block_s, btrace, b);
00091       pc = block->begin;
00092 
00093       /* Add instructions for this block.  */
00094       for (;;)
00095         {
00096           btrace_inst_s *inst;
00097           int size;
00098 
00099           /* We should hit the end of the block.  Warn if we went too far.  */
00100           if (block->end < pc)
00101             {
00102               warning (_("Recorded trace may be corrupted."));
00103               break;
00104             }
00105 
00106           inst = VEC_safe_push (btrace_inst_s, itrace, NULL);
00107           inst->pc = pc;
00108 
00109           /* We're done once we pushed the instruction at the end.  */
00110           if (block->end == pc)
00111             break;
00112 
00113           size = gdb_insn_length (gdbarch, pc);
00114 
00115           /* Make sure we terminate if we fail to compute the size.  */
00116           if (size <= 0)
00117             {
00118               warning (_("Recorded trace may be incomplete."));
00119               break;
00120             }
00121 
00122           pc += size;
00123         }
00124     }
00125 
00126   return itrace;
00127 }
00128 
00129 /* Return the function name of a recorded function segment for printing.
00130    This function never returns NULL.  */
00131 
00132 static const char *
00133 ftrace_print_function_name (struct btrace_func *bfun)
00134 {
00135   struct minimal_symbol *msym;
00136   struct symbol *sym;
00137 
00138   msym = bfun->msym;
00139   sym = bfun->sym;
00140 
00141   if (sym != NULL)
00142     return SYMBOL_PRINT_NAME (sym);
00143 
00144   if (msym != NULL)
00145     return SYMBOL_PRINT_NAME (msym);
00146 
00147   return "<unknown>";
00148 }
00149 
00150 /* Return the file name of a recorded function segment for printing.
00151    This function never returns NULL.  */
00152 
00153 static const char *
00154 ftrace_print_filename (struct btrace_func *bfun)
00155 {
00156   struct symbol *sym;
00157   const char *filename;
00158 
00159   sym = bfun->sym;
00160 
00161   if (sym != NULL)
00162     filename = symtab_to_filename_for_display (sym->symtab);
00163   else
00164     filename = "<unknown>";
00165 
00166   return filename;
00167 }
00168 
00169 /* Print an ftrace debug status message.  */
00170 
00171 static void
00172 ftrace_debug (struct btrace_func *bfun, const char *prefix)
00173 {
00174   DEBUG_FTRACE ("%s: fun = %s, file = %s, lines = [%d; %d], insn = [%u; %u]",
00175                 prefix, ftrace_print_function_name (bfun),
00176                 ftrace_print_filename (bfun), bfun->lbegin, bfun->lend,
00177                 bfun->ibegin, bfun->iend);
00178 }
00179 
00180 /* Initialize a recorded function segment.  */
00181 
00182 static void
00183 ftrace_init_func (struct btrace_func *bfun, struct minimal_symbol *mfun,
00184                   struct symbol *fun, unsigned int idx)
00185 {
00186   bfun->msym = mfun;
00187   bfun->sym = fun;
00188   bfun->lbegin = INT_MAX;
00189   bfun->lend = 0;
00190   bfun->ibegin = idx;
00191   bfun->iend = idx;
00192 }
00193 
00194 /* Check whether the function has changed.  */
00195 
00196 static int
00197 ftrace_function_switched (struct btrace_func *bfun,
00198                           struct minimal_symbol *mfun, struct symbol *fun)
00199 {
00200   struct minimal_symbol *msym;
00201   struct symbol *sym;
00202 
00203   /* The function changed if we did not have one before.  */
00204   if (bfun == NULL)
00205     return 1;
00206 
00207   msym = bfun->msym;
00208   sym = bfun->sym;
00209 
00210   /* If the minimal symbol changed, we certainly switched functions.  */
00211   if (mfun != NULL && msym != NULL
00212       && strcmp (SYMBOL_LINKAGE_NAME (mfun), SYMBOL_LINKAGE_NAME (msym)) != 0)
00213     return 1;
00214 
00215   /* If the symbol changed, we certainly switched functions.  */
00216   if (fun != NULL && sym != NULL)
00217     {
00218       const char *bfname, *fname;
00219 
00220       /* Check the function name.  */
00221       if (strcmp (SYMBOL_LINKAGE_NAME (fun), SYMBOL_LINKAGE_NAME (sym)) != 0)
00222         return 1;
00223 
00224       /* Check the location of those functions, as well.  */
00225       bfname = symtab_to_fullname (sym->symtab);
00226       fname = symtab_to_fullname (fun->symtab);
00227       if (filename_cmp (fname, bfname) != 0)
00228         return 1;
00229     }
00230 
00231   return 0;
00232 }
00233 
00234 /* Check if we should skip this file when generating the function call
00235    history.  We would want to do that if, say, a macro that is defined
00236    in another file is expanded in this function.  */
00237 
00238 static int
00239 ftrace_skip_file (struct btrace_func *bfun, const char *filename)
00240 {
00241   struct symbol *sym;
00242   const char *bfile;
00243 
00244   sym = bfun->sym;
00245 
00246   if (sym != NULL)
00247     bfile = symtab_to_fullname (sym->symtab);
00248   else
00249     bfile = "";
00250 
00251   if (filename == NULL)
00252     filename = "";
00253 
00254   return (filename_cmp (bfile, filename) != 0);
00255 }
00256 
00257 /* Compute the function trace from the instruction trace.  */
00258 
00259 static VEC (btrace_func_s) *
00260 compute_ftrace (VEC (btrace_inst_s) *itrace)
00261 {
00262   VEC (btrace_func_s) *ftrace;
00263   struct btrace_inst *binst;
00264   struct btrace_func *bfun;
00265   unsigned int idx;
00266 
00267   DEBUG ("compute ftrace");
00268 
00269   ftrace = NULL;
00270   bfun = NULL;
00271 
00272   for (idx = 0; VEC_iterate (btrace_inst_s, itrace, idx, binst); ++idx)
00273     {
00274       struct symtab_and_line sal;
00275       struct bound_minimal_symbol mfun;
00276       struct symbol *fun;
00277       const char *filename;
00278       CORE_ADDR pc;
00279 
00280       pc = binst->pc;
00281 
00282       /* Try to determine the function we're in.  We use both types of symbols
00283          to avoid surprises when we sometimes get a full symbol and sometimes
00284          only a minimal symbol.  */
00285       fun = find_pc_function (pc);
00286       mfun = lookup_minimal_symbol_by_pc (pc);
00287 
00288       if (fun == NULL && mfun.minsym == NULL)
00289         {
00290           DEBUG_FTRACE ("no symbol at %u, pc=%s", idx,
00291                         core_addr_to_string_nz (pc));
00292           continue;
00293         }
00294 
00295       /* If we're switching functions, we start over.  */
00296       if (ftrace_function_switched (bfun, mfun.minsym, fun))
00297         {
00298           bfun = VEC_safe_push (btrace_func_s, ftrace, NULL);
00299 
00300           ftrace_init_func (bfun, mfun.minsym, fun, idx);
00301           ftrace_debug (bfun, "init");
00302         }
00303 
00304       /* Update the instruction range.  */
00305       bfun->iend = idx;
00306       ftrace_debug (bfun, "update insns");
00307 
00308       /* Let's see if we have source correlation, as well.  */
00309       sal = find_pc_line (pc, 0);
00310       if (sal.symtab == NULL || sal.line == 0)
00311         {
00312           DEBUG_FTRACE ("no lines at %u, pc=%s", idx,
00313                         core_addr_to_string_nz (pc));
00314           continue;
00315         }
00316 
00317       /* Check if we switched files.  This could happen if, say, a macro that
00318          is defined in another file is expanded here.  */
00319       filename = symtab_to_fullname (sal.symtab);
00320       if (ftrace_skip_file (bfun, filename))
00321         {
00322           DEBUG_FTRACE ("ignoring file at %u, pc=%s, file=%s", idx,
00323                         core_addr_to_string_nz (pc), filename);
00324           continue;
00325         }
00326 
00327       /* Update the line range.  */
00328       bfun->lbegin = min (bfun->lbegin, sal.line);
00329       bfun->lend = max (bfun->lend, sal.line);
00330       ftrace_debug (bfun, "update lines");
00331     }
00332 
00333   return ftrace;
00334 }
00335 
00336 /* See btrace.h.  */
00337 
00338 void
00339 btrace_enable (struct thread_info *tp)
00340 {
00341   if (tp->btrace.target != NULL)
00342     return;
00343 
00344   if (!target_supports_btrace ())
00345     error (_("Target does not support branch tracing."));
00346 
00347   DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
00348 
00349   tp->btrace.target = target_enable_btrace (tp->ptid);
00350 }
00351 
00352 /* See btrace.h.  */
00353 
00354 void
00355 btrace_disable (struct thread_info *tp)
00356 {
00357   struct btrace_thread_info *btp = &tp->btrace;
00358   int errcode = 0;
00359 
00360   if (btp->target == NULL)
00361     return;
00362 
00363   DEBUG ("disable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
00364 
00365   target_disable_btrace (btp->target);
00366   btp->target = NULL;
00367 
00368   btrace_clear (tp);
00369 }
00370 
00371 /* See btrace.h.  */
00372 
00373 void
00374 btrace_teardown (struct thread_info *tp)
00375 {
00376   struct btrace_thread_info *btp = &tp->btrace;
00377   int errcode = 0;
00378 
00379   if (btp->target == NULL)
00380     return;
00381 
00382   DEBUG ("teardown thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
00383 
00384   target_teardown_btrace (btp->target);
00385   btp->target = NULL;
00386 
00387   btrace_clear (tp);
00388 }
00389 
00390 /* See btrace.h.  */
00391 
00392 void
00393 btrace_fetch (struct thread_info *tp)
00394 {
00395   struct btrace_thread_info *btinfo;
00396   VEC (btrace_block_s) *btrace;
00397 
00398   DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
00399 
00400   btinfo = &tp->btrace;
00401   if (btinfo->target == NULL)
00402     return;
00403 
00404   btrace = target_read_btrace (btinfo->target, btrace_read_new);
00405   if (VEC_empty (btrace_block_s, btrace))
00406     return;
00407 
00408   btrace_clear (tp);
00409 
00410   btinfo->btrace = btrace;
00411   btinfo->itrace = compute_itrace (btinfo->btrace);
00412   btinfo->ftrace = compute_ftrace (btinfo->itrace);
00413 
00414   /* Initialize branch trace iterators.  */
00415   btrace_init_insn_iterator (btinfo);
00416   btrace_init_func_iterator (btinfo);
00417 }
00418 
00419 /* See btrace.h.  */
00420 
00421 void
00422 btrace_clear (struct thread_info *tp)
00423 {
00424   struct btrace_thread_info *btinfo;
00425 
00426   DEBUG ("clear thread %d (%s)", tp->num, target_pid_to_str (tp->ptid));
00427 
00428   btinfo = &tp->btrace;
00429 
00430   VEC_free (btrace_block_s, btinfo->btrace);
00431   VEC_free (btrace_inst_s, btinfo->itrace);
00432   VEC_free (btrace_func_s, btinfo->ftrace);
00433 
00434   btinfo->btrace = NULL;
00435   btinfo->itrace = NULL;
00436   btinfo->ftrace = NULL;
00437 }
00438 
00439 /* See btrace.h.  */
00440 
00441 void
00442 btrace_free_objfile (struct objfile *objfile)
00443 {
00444   struct thread_info *tp;
00445 
00446   DEBUG ("free objfile");
00447 
00448   ALL_THREADS (tp)
00449     btrace_clear (tp);
00450 }
00451 
00452 #if defined (HAVE_LIBEXPAT)
00453 
00454 /* Check the btrace document version.  */
00455 
00456 static void
00457 check_xml_btrace_version (struct gdb_xml_parser *parser,
00458                           const struct gdb_xml_element *element,
00459                           void *user_data, VEC (gdb_xml_value_s) *attributes)
00460 {
00461   const char *version = xml_find_attribute (attributes, "version")->value;
00462 
00463   if (strcmp (version, "1.0") != 0)
00464     gdb_xml_error (parser, _("Unsupported btrace version: \"%s\""), version);
00465 }
00466 
00467 /* Parse a btrace "block" xml record.  */
00468 
00469 static void
00470 parse_xml_btrace_block (struct gdb_xml_parser *parser,
00471                         const struct gdb_xml_element *element,
00472                         void *user_data, VEC (gdb_xml_value_s) *attributes)
00473 {
00474   VEC (btrace_block_s) **btrace;
00475   struct btrace_block *block;
00476   ULONGEST *begin, *end;
00477 
00478   btrace = user_data;
00479   block = VEC_safe_push (btrace_block_s, *btrace, NULL);
00480 
00481   begin = xml_find_attribute (attributes, "begin")->value;
00482   end = xml_find_attribute (attributes, "end")->value;
00483 
00484   block->begin = *begin;
00485   block->end = *end;
00486 }
00487 
00488 static const struct gdb_xml_attribute block_attributes[] = {
00489   { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
00490   { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
00491   { NULL, GDB_XML_AF_NONE, NULL, NULL }
00492 };
00493 
00494 static const struct gdb_xml_attribute btrace_attributes[] = {
00495   { "version", GDB_XML_AF_NONE, NULL, NULL },
00496   { NULL, GDB_XML_AF_NONE, NULL, NULL }
00497 };
00498 
00499 static const struct gdb_xml_element btrace_children[] = {
00500   { "block", block_attributes, NULL,
00501     GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, parse_xml_btrace_block, NULL },
00502   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
00503 };
00504 
00505 static const struct gdb_xml_element btrace_elements[] = {
00506   { "btrace", btrace_attributes, btrace_children, GDB_XML_EF_NONE,
00507     check_xml_btrace_version, NULL },
00508   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
00509 };
00510 
00511 #endif /* defined (HAVE_LIBEXPAT) */
00512 
00513 /* See btrace.h.  */
00514 
00515 VEC (btrace_block_s) *
00516 parse_xml_btrace (const char *buffer)
00517 {
00518   VEC (btrace_block_s) *btrace = NULL;
00519   struct cleanup *cleanup;
00520   int errcode;
00521 
00522 #if defined (HAVE_LIBEXPAT)
00523 
00524   cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace);
00525   errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
00526                                  buffer, &btrace);
00527   if (errcode != 0)
00528     {
00529       do_cleanups (cleanup);
00530       return NULL;
00531     }
00532 
00533   /* Keep parse results.  */
00534   discard_cleanups (cleanup);
00535 
00536 #else  /* !defined (HAVE_LIBEXPAT) */
00537 
00538   error (_("Cannot process branch trace.  XML parsing is not supported."));
00539 
00540 #endif  /* !defined (HAVE_LIBEXPAT) */
00541 
00542   return btrace;
00543 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines