X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fbtrace.c;h=206e692876fa630a1e0ae0ba2c67d7d04d3b1c71;hb=31fd9caad9fa8e13bbc132dce264f0c3bc53412f;hp=87a171e812eb9b212679bb7b72275b73cf40fdab;hpb=034f788c5ec1e5b20fae3b6efc7fe961631e014d;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/btrace.c b/gdb/btrace.c index 87a171e812..206e692876 100644 --- a/gdb/btrace.c +++ b/gdb/btrace.c @@ -1,6 +1,6 @@ /* Branch trace support for GDB, the GNU debugger. - Copyright (C) 2013-2014 Free Software Foundation, Inc. + Copyright (C) 2013-2015 Free Software Foundation, Inc. Contributed by Intel Corp. @@ -19,9 +19,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include "defs.h" #include "btrace.h" #include "gdbthread.h" -#include "exceptions.h" #include "inferior.h" #include "target.h" #include "record.h" @@ -79,7 +79,7 @@ ftrace_print_filename (const struct btrace_function *bfun) sym = bfun->sym; if (sym != NULL) - filename = symtab_to_filename_for_display (sym->symtab); + filename = symtab_to_filename_for_display (symbol_symtab (sym)); else filename = ""; @@ -151,8 +151,8 @@ ftrace_function_switched (const struct btrace_function *bfun, return 1; /* Check the location of those functions, as well. */ - bfname = symtab_to_fullname (sym->symtab); - fname = symtab_to_fullname (fun->symtab); + bfname = symtab_to_fullname (symbol_symtab (sym)); + fname = symtab_to_fullname (symbol_symtab (fun)); if (filename_cmp (fname, bfname) != 0) return 1; } @@ -183,7 +183,7 @@ ftrace_skip_file (const struct btrace_function *bfun, const char *fullname) if (sym == NULL) return 1; - bfile = symtab_to_fullname (sym->symtab); + bfile = symtab_to_fullname (symbol_symtab (sym)); return (filename_cmp (bfile, fullname) != 0); } @@ -223,6 +223,7 @@ ftrace_new_function (struct btrace_function *prev, bfun->number = prev->number + 1; bfun->insn_offset = (prev->insn_offset + VEC_length (btrace_insn_s, prev->insn)); + bfun->level = prev->level; } return bfun; @@ -276,7 +277,7 @@ ftrace_new_call (struct btrace_function *caller, bfun = ftrace_new_function (caller, mfun, fun); bfun->up = caller; - bfun->level = caller->level + 1; + bfun->level += 1; ftrace_debug (bfun, "new call"); @@ -296,7 +297,7 @@ ftrace_new_tailcall (struct btrace_function *caller, bfun = ftrace_new_function (caller, mfun, fun); bfun->up = caller; - bfun->level = caller->level + 1; + bfun->level += 1; bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL; ftrace_debug (bfun, "new tail call"); @@ -330,20 +331,19 @@ ftrace_find_caller (struct btrace_function *bfun, tail calls ending with a jump). */ static struct btrace_function * -ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun) +ftrace_find_call (struct btrace_function *bfun) { for (; bfun != NULL; bfun = bfun->up) { struct btrace_insn *last; - CORE_ADDR pc; - /* We do not allow empty function segments. */ - gdb_assert (!VEC_empty (btrace_insn_s, bfun->insn)); + /* Skip gaps. */ + if (bfun->errcode != 0) + continue; last = VEC_last (btrace_insn_s, bfun->insn); - pc = last->pc; - if (gdbarch_insn_is_call (gdbarch, pc)) + if (last->iclass == BTRACE_INSN_CALL) break; } @@ -355,8 +355,7 @@ ftrace_find_call (struct gdbarch *gdbarch, struct btrace_function *bfun) MFUN and FUN are the symbol information we have for this function. */ static struct btrace_function * -ftrace_new_return (struct gdbarch *gdbarch, - struct btrace_function *prev, +ftrace_new_return (struct btrace_function *prev, struct minimal_symbol *mfun, struct symbol *fun) { @@ -391,7 +390,7 @@ ftrace_new_return (struct gdbarch *gdbarch, wrong or that the call is simply not included in the trace. */ /* Let's search for some actual call. */ - caller = ftrace_find_call (gdbarch, prev->up); + caller = ftrace_find_call (prev->up); if (caller == NULL) { /* There is no call in PREV's back trace. We assume that the @@ -441,21 +440,40 @@ ftrace_new_switch (struct btrace_function *prev, be wrong at this point. */ bfun = ftrace_new_function (prev, mfun, fun); - /* We keep the function level. */ - bfun->level = prev->level; - ftrace_debug (bfun, "new switch"); return bfun; } +/* Add a new function segment for a gap in the trace due to a decode error. + PREV is the chronologically preceding function segment. + ERRCODE is the format-specific error code. */ + +static struct btrace_function * +ftrace_new_gap (struct btrace_function *prev, int errcode) +{ + struct btrace_function *bfun; + + /* We hijack prev if it was empty. */ + if (prev != NULL && prev->errcode == 0 + && VEC_empty (btrace_insn_s, prev->insn)) + bfun = prev; + else + bfun = ftrace_new_function (prev, NULL, NULL); + + bfun->errcode = errcode; + + ftrace_debug (bfun, "new gap"); + + return bfun; +} + /* Update BFUN with respect to the instruction at PC. This may create new function segments. Return the chronologically latest function segment, never NULL. */ static struct btrace_function * -ftrace_update_function (struct gdbarch *gdbarch, - struct btrace_function *bfun, CORE_ADDR pc) +ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc) { struct bound_minimal_symbol bmfun; struct minimal_symbol *mfun; @@ -472,8 +490,8 @@ ftrace_update_function (struct gdbarch *gdbarch, if (fun == NULL && mfun == NULL) DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc)); - /* If we didn't have a function before, we create one. */ - if (bfun == NULL) + /* If we didn't have a function or if we had a gap before, we create one. */ + if (bfun == NULL || bfun->errcode != 0) return ftrace_new_function (bfun, mfun, fun); /* Check the last instruction, if we have one. @@ -485,24 +503,29 @@ ftrace_update_function (struct gdbarch *gdbarch, if (last != NULL) { - CORE_ADDR lpc; + switch (last->iclass) + { + case BTRACE_INSN_RETURN: + return ftrace_new_return (bfun, mfun, fun); - lpc = last->pc; + case BTRACE_INSN_CALL: + /* Ignore calls to the next instruction. They are used for PIC. */ + if (last->pc + last->size == pc) + break; - /* Check for returns. */ - if (gdbarch_insn_is_ret (gdbarch, lpc)) - return ftrace_new_return (gdbarch, bfun, mfun, fun); + return ftrace_new_call (bfun, mfun, fun); - /* Check for calls. */ - if (gdbarch_insn_is_call (gdbarch, lpc)) - { - int size; + case BTRACE_INSN_JUMP: + { + CORE_ADDR start; - size = gdb_insn_length (gdbarch, lpc); + start = get_pc_function_start (pc); - /* Ignore calls to the next instruction. They are used for PIC. */ - if (lpc + size != pc) - return ftrace_new_call (bfun, mfun, fun); + /* If we can't determine the function for PC, we treat a jump at + the end of the block as tail call. */ + if (start == 0 || start == pc) + return ftrace_new_tailcall (bfun, mfun, fun); + } } } @@ -514,24 +537,6 @@ ftrace_update_function (struct gdbarch *gdbarch, ftrace_print_function_name (bfun), ftrace_print_filename (bfun)); - if (last != NULL) - { - CORE_ADDR start, lpc; - - start = get_pc_function_start (pc); - - /* If we can't determine the function for PC, we treat a jump at - the end of the block as tail call. */ - if (start == 0) - start = pc; - - lpc = last->pc; - - /* Jumps indicate optimized tail calls. */ - if (start == pc && gdbarch_insn_is_jump (gdbarch, lpc)) - return ftrace_new_tailcall (bfun, mfun, fun); - } - return ftrace_new_switch (bfun, mfun, fun); } @@ -574,36 +579,56 @@ ftrace_update_lines (struct btrace_function *bfun, CORE_ADDR pc) /* Add the instruction at PC to BFUN's instructions. */ static void -ftrace_update_insns (struct btrace_function *bfun, CORE_ADDR pc) +ftrace_update_insns (struct btrace_function *bfun, + const struct btrace_insn *insn) { - struct btrace_insn *insn; - - insn = VEC_safe_push (btrace_insn_s, bfun->insn, NULL); - insn->pc = pc; + VEC_safe_push (btrace_insn_s, bfun->insn, insn); if (record_debug > 1) ftrace_debug (bfun, "update insn"); } -/* Compute the function branch trace from a block branch trace BTRACE for - a thread given by BTINFO. */ +/* Classify the instruction at PC. */ + +static enum btrace_insn_class +ftrace_classify_insn (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + volatile struct gdb_exception error; + enum btrace_insn_class iclass; + + iclass = BTRACE_INSN_OTHER; + TRY_CATCH (error, RETURN_MASK_ERROR) + { + if (gdbarch_insn_is_call (gdbarch, pc)) + iclass = BTRACE_INSN_CALL; + else if (gdbarch_insn_is_ret (gdbarch, pc)) + iclass = BTRACE_INSN_RETURN; + else if (gdbarch_insn_is_jump (gdbarch, pc)) + iclass = BTRACE_INSN_JUMP; + } + + return iclass; +} + +/* Compute the function branch trace from BTS trace. */ static void -btrace_compute_ftrace (struct btrace_thread_info *btinfo, - VEC (btrace_block_s) *btrace) +btrace_compute_ftrace_bts (struct thread_info *tp, + const struct btrace_data_bts *btrace) { + struct btrace_thread_info *btinfo; struct btrace_function *begin, *end; struct gdbarch *gdbarch; - unsigned int blk; + unsigned int blk, ngaps; int level; - DEBUG ("compute ftrace"); - gdbarch = target_gdbarch (); + btinfo = &tp->btrace; begin = btinfo->begin; end = btinfo->end; + ngaps = btinfo->ngaps; level = begin != NULL ? -btinfo->level : INT_MAX; - blk = VEC_length (btrace_block_s, btrace); + blk = VEC_length (btrace_block_s, btrace->blocks); while (blk != 0) { @@ -612,22 +637,32 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo, blk -= 1; - block = VEC_index (btrace_block_s, btrace, blk); + block = VEC_index (btrace_block_s, btrace->blocks, blk); pc = block->begin; for (;;) { + volatile struct gdb_exception error; + struct btrace_insn insn; int size; /* We should hit the end of the block. Warn if we went too far. */ if (block->end < pc) { - warning (_("Recorded trace may be corrupted around %s."), - core_addr_to_string_nz (pc)); + /* Indicate the gap in the trace - unless we're at the + beginning. */ + if (begin != NULL) + { + warning (_("Recorded trace may be corrupted around %s."), + core_addr_to_string_nz (pc)); + + end = ftrace_new_gap (end, BDE_BTS_OVERFLOW); + ngaps += 1; + } break; } - end = ftrace_update_function (gdbarch, end, pc); + end = ftrace_update_function (end, pc); if (begin == NULL) begin = end; @@ -636,20 +671,32 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo, if (blk != 0) level = min (level, end->level); - ftrace_update_insns (end, pc); + size = 0; + TRY_CATCH (error, RETURN_MASK_ERROR) + size = gdb_insn_length (gdbarch, pc); + + insn.pc = pc; + insn.size = size; + insn.iclass = ftrace_classify_insn (gdbarch, pc); + + ftrace_update_insns (end, &insn); ftrace_update_lines (end, pc); /* We're done once we pushed the instruction at the end. */ if (block->end == pc) break; - size = gdb_insn_length (gdbarch, pc); - - /* Make sure we terminate if we fail to compute the size. */ + /* We can't continue if we fail to compute the size. */ if (size <= 0) { warning (_("Recorded trace may be incomplete around %s."), core_addr_to_string_nz (pc)); + + /* Indicate the gap in the trace. We just added INSN so we're + not at the beginning. */ + end = ftrace_new_gap (end, BDE_BTS_INSN_SIZE); + ngaps += 1; + break; } @@ -668,6 +715,7 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo, btinfo->begin = begin; btinfo->end = end; + btinfo->ngaps = ngaps; /* LEVEL is the minimal function level of all btrace function segments. Define the global level offset to -LEVEL so all function levels are @@ -675,12 +723,33 @@ btrace_compute_ftrace (struct btrace_thread_info *btinfo, btinfo->level = -level; } +/* Compute the function branch trace from a block branch trace BTRACE for + a thread given by BTINFO. */ + +static void +btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace) +{ + DEBUG ("compute ftrace"); + + switch (btrace->format) + { + case BTRACE_FORMAT_NONE: + return; + + case BTRACE_FORMAT_BTS: + btrace_compute_ftrace_bts (tp, &btrace->variant.bts); + return; + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} + /* Add an entry for the current PC. */ static void btrace_add_pc (struct thread_info *tp) { - VEC (btrace_block_s) *btrace; + struct btrace_data btrace; struct btrace_block *block; struct regcache *regcache; struct cleanup *cleanup; @@ -689,14 +758,17 @@ btrace_add_pc (struct thread_info *tp) regcache = get_thread_regcache (tp->ptid); pc = regcache_read_pc (regcache); - btrace = NULL; - cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace); + btrace_data_init (&btrace); + btrace.format = BTRACE_FORMAT_BTS; + btrace.variant.bts.blocks = NULL; + + cleanup = make_cleanup_btrace_data (&btrace); - block = VEC_safe_push (btrace_block_s, btrace, NULL); + block = VEC_safe_push (btrace_block_s, btrace.variant.bts.blocks, NULL); block->begin = pc; block->end = pc; - btrace_compute_ftrace (&tp->btrace, btrace); + btrace_compute_ftrace (tp, &btrace); do_cleanups (cleanup); } @@ -704,17 +776,17 @@ btrace_add_pc (struct thread_info *tp) /* See btrace.h. */ void -btrace_enable (struct thread_info *tp) +btrace_enable (struct thread_info *tp, const struct btrace_config *conf) { if (tp->btrace.target != NULL) return; - if (!target_supports_btrace ()) + if (!target_supports_btrace (conf->format)) error (_("Target does not support branch tracing.")); DEBUG ("enable thread %d (%s)", tp->num, target_pid_to_str (tp->ptid)); - tp->btrace.target = target_enable_btrace (tp->ptid); + tp->btrace.target = target_enable_btrace (tp->ptid, conf); /* Add an entry for the current PC so we start tracing from where we enabled it. */ @@ -724,6 +796,17 @@ btrace_enable (struct thread_info *tp) /* See btrace.h. */ +const struct btrace_config * +btrace_conf (const struct btrace_thread_info *btinfo) +{ + if (btinfo->target == NULL) + return NULL; + + return target_btrace_conf (btinfo->target); +} + +/* See btrace.h. */ + void btrace_disable (struct thread_info *tp) { @@ -760,31 +843,34 @@ btrace_teardown (struct thread_info *tp) btrace_clear (tp); } -/* Adjust the block trace in order to stitch old and new trace together. - BTRACE is the new delta trace between the last and the current stop. - BTINFO is the old branch trace until the last stop. - May modify BTRACE as well as the existing trace in BTINFO. - Return 0 on success, -1 otherwise. */ +/* Stitch branch trace in BTS format. */ static int -btrace_stitch_trace (VEC (btrace_block_s) **btrace, - const struct btrace_thread_info *btinfo) +btrace_stitch_bts (struct btrace_data_bts *btrace, struct thread_info *tp) { + struct btrace_thread_info *btinfo; struct btrace_function *last_bfun; struct btrace_insn *last_insn; btrace_block_s *first_new_block; - /* If we don't have trace, there's nothing to do. */ - if (VEC_empty (btrace_block_s, *btrace)) - return 0; - + btinfo = &tp->btrace; last_bfun = btinfo->end; gdb_assert (last_bfun != NULL); + gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks)); + + /* If the existing trace ends with a gap, we just glue the traces + together. We need to drop the last (i.e. chronologically first) block + of the new trace, though, since we can't fill in the start address.*/ + if (VEC_empty (btrace_insn_s, last_bfun->insn)) + { + VEC_pop (btrace_block_s, btrace->blocks); + return 0; + } /* Beware that block trace starts with the most recent block, so the chronologically first block in the new trace is the last block in the new trace's block vector. */ - first_new_block = VEC_last (btrace_block_s, *btrace); + first_new_block = VEC_last (btrace_block_s, btrace->blocks); last_insn = VEC_last (btrace_insn_s, last_bfun->insn); /* If the current PC at the end of the block is the same as in our current @@ -796,9 +882,9 @@ btrace_stitch_trace (VEC (btrace_block_s) **btrace, In the second case, the delta trace vector should contain exactly one entry for the partial block containing the current PC. Remove it. */ if (first_new_block->end == last_insn->pc - && VEC_length (btrace_block_s, *btrace) == 1) + && VEC_length (btrace_block_s, btrace->blocks) == 1) { - VEC_pop (btrace_block_s, *btrace); + VEC_pop (btrace_block_s, btrace->blocks); return 0; } @@ -831,9 +917,42 @@ btrace_stitch_trace (VEC (btrace_block_s) **btrace, been the only instruction in this function segment. This violates the invariant but will be remedied shortly by btrace_compute_ftrace when we add the new trace. */ + + /* The only case where this would hurt is if the entire trace consisted + of just that one instruction. If we remove it, we might turn the now + empty btrace function segment into a gap. But we don't want gaps at + the beginning. To avoid this, we remove the entire old trace. */ + if (last_bfun == btinfo->begin && VEC_empty (btrace_insn_s, last_bfun->insn)) + btrace_clear (tp); + return 0; } +/* Adjust the block trace in order to stitch old and new trace together. + BTRACE is the new delta trace between the last and the current stop. + TP is the traced thread. + May modifx BTRACE as well as the existing trace in TP. + Return 0 on success, -1 otherwise. */ + +static int +btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp) +{ + /* If we don't have trace, there's nothing to do. */ + if (btrace_data_empty (btrace)) + return 0; + + switch (btrace->format) + { + case BTRACE_FORMAT_NONE: + return 0; + + case BTRACE_FORMAT_BTS: + return btrace_stitch_bts (&btrace->variant.bts, tp); + } + + internal_error (__FILE__, __LINE__, _("Unkown branch trace format.")); +} + /* Clear the branch trace histories in BTINFO. */ static void @@ -855,13 +974,12 @@ btrace_fetch (struct thread_info *tp) { struct btrace_thread_info *btinfo; struct btrace_target_info *tinfo; - VEC (btrace_block_s) *btrace; + struct btrace_data btrace; struct cleanup *cleanup; int errcode; DEBUG ("fetch thread %d (%s)", tp->num, target_pid_to_str (tp->ptid)); - btrace = NULL; btinfo = &tp->btrace; tinfo = btinfo->target; if (tinfo == NULL) @@ -873,7 +991,8 @@ btrace_fetch (struct thread_info *tp) if (btinfo->replay != NULL) return; - cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace); + btrace_data_init (&btrace); + cleanup = make_cleanup_btrace_data (&btrace); /* Let's first try to extend the trace we already have. */ if (btinfo->end != NULL) @@ -882,7 +1001,7 @@ btrace_fetch (struct thread_info *tp) if (errcode == 0) { /* Success. Let's try to stitch the traces together. */ - errcode = btrace_stitch_trace (&btrace, btinfo); + errcode = btrace_stitch_trace (&btrace, tp); } else { @@ -890,7 +1009,7 @@ btrace_fetch (struct thread_info *tp) errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_NEW); /* If we got any new trace, discard what we have. */ - if (errcode == 0 && !VEC_empty (btrace_block_s, btrace)) + if (errcode == 0 && !btrace_data_empty (&btrace)) btrace_clear (tp); } @@ -909,10 +1028,10 @@ btrace_fetch (struct thread_info *tp) error (_("Failed to read branch trace.")); /* Compute the trace, provided we have any. */ - if (!VEC_empty (btrace_block_s, btrace)) + if (!btrace_data_empty (&btrace)) { btrace_clear_history (btinfo); - btrace_compute_ftrace (btinfo, btrace); + btrace_compute_ftrace (tp, &btrace); } do_cleanups (cleanup); @@ -945,6 +1064,7 @@ btrace_clear (struct thread_info *tp) btinfo->begin = NULL; btinfo->end = NULL; + btinfo->ngaps = 0; btrace_clear_history (btinfo); } @@ -984,16 +1104,30 @@ parse_xml_btrace_block (struct gdb_xml_parser *parser, const struct gdb_xml_element *element, void *user_data, VEC (gdb_xml_value_s) *attributes) { - VEC (btrace_block_s) **btrace; + struct btrace_data *btrace; struct btrace_block *block; ULONGEST *begin, *end; btrace = user_data; - block = VEC_safe_push (btrace_block_s, *btrace, NULL); + + switch (btrace->format) + { + case BTRACE_FORMAT_BTS: + break; + + case BTRACE_FORMAT_NONE: + btrace->format = BTRACE_FORMAT_BTS; + btrace->variant.bts.blocks = NULL; + break; + + default: + gdb_xml_error (parser, _("Btrace format error.")); + } begin = xml_find_attribute (attributes, "begin")->value; end = xml_find_attribute (attributes, "end")->value; + block = VEC_safe_push (btrace_block_s, btrace->variant.bts.blocks, NULL); block->begin = *begin; block->end = *end; } @@ -1025,18 +1159,19 @@ static const struct gdb_xml_element btrace_elements[] = { /* See btrace.h. */ -VEC (btrace_block_s) * -parse_xml_btrace (const char *buffer) +void +parse_xml_btrace (struct btrace_data *btrace, const char *buffer) { - VEC (btrace_block_s) *btrace = NULL; struct cleanup *cleanup; int errcode; #if defined (HAVE_LIBEXPAT) - cleanup = make_cleanup (VEC_cleanup (btrace_block_s), &btrace); + btrace->format = BTRACE_FORMAT_NONE; + + cleanup = make_cleanup_btrace_data (btrace); errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements, - buffer, &btrace); + buffer, btrace); if (errcode != 0) error (_("Error parsing branch trace.")); @@ -1048,8 +1183,72 @@ parse_xml_btrace (const char *buffer) error (_("Cannot process branch trace. XML parsing is not supported.")); #endif /* !defined (HAVE_LIBEXPAT) */ +} + +#if defined (HAVE_LIBEXPAT) + +/* Parse a btrace-conf "bts" xml record. */ + +static void +parse_xml_btrace_conf_bts (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, VEC (gdb_xml_value_s) *attributes) +{ + struct btrace_config *conf; + struct gdb_xml_value *size; + + conf = user_data; + conf->format = BTRACE_FORMAT_BTS; + conf->bts.size = 0; + + size = xml_find_attribute (attributes, "size"); + if (size != NULL) + conf->bts.size = (unsigned int) * (ULONGEST *) size->value; +} + +static const struct gdb_xml_attribute btrace_conf_bts_attributes[] = { + { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; - return btrace; +static const struct gdb_xml_element btrace_conf_children[] = { + { "bts", btrace_conf_bts_attributes, NULL, GDB_XML_EF_OPTIONAL, + parse_xml_btrace_conf_bts, NULL }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_attribute btrace_conf_attributes[] = { + { "version", GDB_XML_AF_NONE, NULL, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_element btrace_conf_elements[] = { + { "btrace-conf", btrace_conf_attributes, btrace_conf_children, + GDB_XML_EF_NONE, NULL, NULL }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +#endif /* defined (HAVE_LIBEXPAT) */ + +/* See btrace.h. */ + +void +parse_xml_btrace_conf (struct btrace_config *conf, const char *xml) +{ + int errcode; + +#if defined (HAVE_LIBEXPAT) + + errcode = gdb_xml_parse_quick (_("btrace-conf"), "btrace-conf.dtd", + btrace_conf_elements, xml, conf); + if (errcode != 0) + error (_("Error parsing branch trace configuration.")); + +#else /* !defined (HAVE_LIBEXPAT) */ + + error (_("XML parsing is not supported.")); + +#endif /* !defined (HAVE_LIBEXPAT) */ } /* See btrace.h. */ @@ -1063,6 +1262,10 @@ btrace_insn_get (const struct btrace_insn_iterator *it) index = it->index; bfun = it->function; + /* Check if the iterator points to a gap in the trace. */ + if (bfun->errcode != 0) + return NULL; + /* The index is within the bounds of this function's instruction vector. */ end = VEC_length (btrace_insn_s, bfun->insn); gdb_assert (0 < end); @@ -1079,6 +1282,11 @@ btrace_insn_number (const struct btrace_insn_iterator *it) const struct btrace_function *bfun; bfun = it->function; + + /* Return zero if the iterator points to a gap in the trace. */ + if (bfun->errcode != 0) + return 0; + return bfun->insn_offset + it->index; } @@ -1111,12 +1319,16 @@ btrace_insn_end (struct btrace_insn_iterator *it, if (bfun == NULL) error (_("No trace.")); - /* The last instruction in the last function is the current instruction. - We point to it - it is one past the end of the execution trace. */ length = VEC_length (btrace_insn_s, bfun->insn); + /* The last function may either be a gap or it contains the current + instruction, which is one past the end of the execution trace; ignore + it. */ + if (length > 0) + length -= 1; + it->function = bfun; - it->index = length - 1; + it->index = length; } /* See btrace.h. */ @@ -1137,6 +1349,25 @@ btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride) end = VEC_length (btrace_insn_s, bfun->insn); + /* An empty function segment represents a gap in the trace. We count + it as one instruction. */ + if (end == 0) + { + const struct btrace_function *next; + + next = bfun->flow.next; + if (next == NULL) + break; + + stride -= 1; + steps += 1; + + bfun = next; + index = 0; + + continue; + } + gdb_assert (0 < end); gdb_assert (index < end); @@ -1211,12 +1442,20 @@ btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride) bfun = prev; index = VEC_length (btrace_insn_s, bfun->insn); - /* There is at least one instruction in this function segment. */ - gdb_assert (index > 0); + /* An empty function segment represents a gap in the trace. We count + it as one instruction. */ + if (index == 0) + { + stride -= 1; + steps += 1; + + continue; + } } /* Advance the iterator as far as possible within this segment. */ adv = min (index, stride); + stride -= adv; index -= adv; steps += adv; @@ -1243,6 +1482,37 @@ btrace_insn_cmp (const struct btrace_insn_iterator *lhs, lnum = btrace_insn_number (lhs); rnum = btrace_insn_number (rhs); + /* A gap has an instruction number of zero. Things are getting more + complicated if gaps are involved. + + We take the instruction number offset from the iterator's function. + This is the number of the first instruction after the gap. + + This is OK as long as both lhs and rhs point to gaps. If only one of + them does, we need to adjust the number based on the other's regular + instruction number. Otherwise, a gap might compare equal to an + instruction. */ + + if (lnum == 0 && rnum == 0) + { + lnum = lhs->function->insn_offset; + rnum = rhs->function->insn_offset; + } + else if (lnum == 0) + { + lnum = lhs->function->insn_offset; + + if (lnum == rnum) + lnum -= 1; + } + else if (rnum == 0) + { + rnum = rhs->function->insn_offset; + + if (rnum == lnum) + rnum -= 1; + } + return (int) (lnum - rnum); } @@ -1254,16 +1524,25 @@ btrace_find_insn_by_number (struct btrace_insn_iterator *it, unsigned int number) { const struct btrace_function *bfun; - unsigned int end; + unsigned int end, length; for (bfun = btinfo->end; bfun != NULL; bfun = bfun->flow.prev) - if (bfun->insn_offset <= number) - break; + { + /* Skip gaps. */ + if (bfun->errcode != 0) + continue; + + if (bfun->insn_offset <= number) + break; + } if (bfun == NULL) return 0; - end = bfun->insn_offset + VEC_length (btrace_insn_s, bfun->insn); + length = VEC_length (btrace_insn_s, bfun->insn); + gdb_assert (length > 0); + + end = bfun->insn_offset + length; if (end <= number) return 0; @@ -1526,3 +1805,19 @@ btrace_is_empty (struct thread_info *tp) return btrace_insn_cmp (&begin, &end) == 0; } + +/* Forward the cleanup request. */ + +static void +do_btrace_data_cleanup (void *arg) +{ + btrace_data_fini (arg); +} + +/* See btrace.h. */ + +struct cleanup * +make_cleanup_btrace_data (struct btrace_data *data) +{ + return make_cleanup (do_btrace_data_cleanup, data); +}