record-btrace: indicate gaps
authorMarkus Metzger <markus.t.metzger@intel.com>
Thu, 30 Jan 2014 08:51:10 +0000 (09:51 +0100)
committerMarkus Metzger <markus.t.metzger@intel.com>
Mon, 9 Feb 2015 08:52:10 +0000 (09:52 +0100)
Indicate gaps in the trace due to decode errors.  Internally, a gap is
represented as a btrace function segment without instructions and with a
non-zero format-specific error code.

Show the gap when traversing the instruction or function call history.
Also indicate gaps in "info record".

It looks like this:

  (gdb) info record
  Active record target: record-btrace
  Recording format: Branch Trace Store.
  Buffer size: 64KB.
  Recorded 32 instructions in 5 functions (1 gaps) for thread 1 (process 7182).
  (gdb) record function-call-history /cli
  1 fib inst 1,9 at src/fib.c:9,14
  2   fib inst 10,20 at src/fib.c:6,14
  3 [decode error (1): instruction overflow]
  4 fib inst 21,28 at src/fib.c:11,14
  5   fib inst 29,33 at src/fib.c:6,9
  (gdb) record instruction-history 20,22
  20    0x000000000040062f <fib+47>: sub    $0x1,%rax
  [decode error (1): instruction overflow]
  21    0x0000000000400613 <fib+19>: add    $0x1,%rax
  22    0x0000000000400617 <fib+23>: mov    %rax,0x200a3a(%rip)
  (gdb)

Gaps are ignored during reverse execution and replay.

2015-02-09  Markus Metzger  <markus.t.metzger@intel.com>

* btrace.c (ftrace_find_call): Skip gaps.
(ftrace_new_function): Initialize level.
(ftrace_new_call, ftrace_new_tailcall, ftrace_new_return)
(ftrace_new_switch): Update
level computation.
(ftrace_new_gap): New.
(ftrace_update_function): Create new function after gap.
(btrace_compute_ftrace_bts): Create gap on error.
(btrace_stitch_bts): Update parameters.  Clear trace if it
becomes empty.
(btrace_stitch_trace): Update parameters.  Update callers.
(btrace_clear): Reset the number of gaps.
(btrace_insn_get): Return NULL if the iterator points to a gap.
(btrace_insn_number): Return zero if the iterator points to a gap.
(btrace_insn_end): Allow gaps at the end.
(btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle gaps.
(btrace_find_insn_by_number): Assert that the found iterator does
not point to a gap.
(btrace_call_next, btrace_call_prev): Assert that the last function
is not a gap.
* btrace.h (btrace_bts_error): New.
(btrace_function): Update comment.
(btrace_function) <insn, insn_offset, number>: Update comment.
(btrace_function) <errcode>: New.
(btrace_thread_info) <ngaps>: New.
(btrace_thread_info) <replay>: Update comment.
(btrace_insn_get): Update comment.
* record-btrace.c (btrace_ui_out_decode_error): New.
(record_btrace_info): Print number of gaps.
(btrace_insn_history, btrace_call_history): Call
btrace_ui_out_decode_error for gaps.
(record_btrace_step_thread, record_btrace_start_replaying): Skip gaps.

testsuite/
* gdb.btrace/buffer-size.exp: Update "info record" output.
* gdb.btrace/delta.exp: Update "info record" output.
* gdb.btrace/enable.exp: Update "info record" output.
* gdb.btrace/finish.exp: Update "info record" output.
* gdb.btrace/instruction_history.exp: Update "info record" output.
* gdb.btrace/next.exp: Update "info record" output.
* gdb.btrace/nexti.exp: Update "info record" output.
* gdb.btrace/step.exp: Update "info record" output.
* gdb.btrace/stepi.exp: Update "info record" output.
* gdb.btrace/nohist.exp: Update "info record" output.

15 files changed:
gdb/ChangeLog
gdb/btrace.c
gdb/btrace.h
gdb/record-btrace.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.btrace/buffer-size.exp
gdb/testsuite/gdb.btrace/delta.exp
gdb/testsuite/gdb.btrace/enable.exp
gdb/testsuite/gdb.btrace/finish.exp
gdb/testsuite/gdb.btrace/instruction_history.exp
gdb/testsuite/gdb.btrace/next.exp
gdb/testsuite/gdb.btrace/nexti.exp
gdb/testsuite/gdb.btrace/nohist.exp
gdb/testsuite/gdb.btrace/step.exp
gdb/testsuite/gdb.btrace/stepi.exp

index 89521dab1ac45e13c9ae748a09f6951a321467e4..dedbb70be59abe466dc6ad2e5621c28bc5f575a6 100644 (file)
@@ -1,3 +1,38 @@
+2015-02-09  Markus Metzger  <markus.t.metzger@intel.com>
+
+       * btrace.c (ftrace_find_call): Skip gaps.
+       (ftrace_new_function): Initialize level.
+       (ftrace_new_call, ftrace_new_tailcall, ftrace_new_return)
+       (ftrace_new_switch): Update
+       level computation.
+       (ftrace_new_gap): New.
+       (ftrace_update_function): Create new function after gap.
+       (btrace_compute_ftrace_bts): Create gap on error.
+       (btrace_stitch_bts): Update parameters.  Clear trace if it
+       becomes empty.
+       (btrace_stitch_trace): Update parameters.  Update callers.
+       (btrace_clear): Reset the number of gaps.
+       (btrace_insn_get): Return NULL if the iterator points to a gap.
+       (btrace_insn_number): Return zero if the iterator points to a gap.
+       (btrace_insn_end): Allow gaps at the end.
+       (btrace_insn_next, btrace_insn_prev, btrace_insn_cmp): Handle gaps.
+       (btrace_find_insn_by_number): Assert that the found iterator does
+       not point to a gap.
+       (btrace_call_next, btrace_call_prev): Assert that the last function
+       is not a gap.
+       * btrace.h (btrace_bts_error): New.
+       (btrace_function): Update comment.
+       (btrace_function) <insn, insn_offset, number>: Update comment.
+       (btrace_function) <errcode>: New.
+       (btrace_thread_info) <ngaps>: New.
+       (btrace_thread_info) <replay>: Update comment.
+       (btrace_insn_get): Update comment.
+       * record-btrace.c (btrace_ui_out_decode_error): New.
+       (record_btrace_info): Print number of gaps.
+       (btrace_insn_history, btrace_call_history): Call
+       btrace_ui_out_decode_error for gaps.
+       (record_btrace_step_thread, record_btrace_start_replaying): Skip gaps.
+
 2015-02-09  Markus Metzger  <markus.t.metzger@intel.com>
 
        * common/btrace-common.h (btrace_cpu_vendor, btrace_cpu): New.
index 72e85670d1ddb2c7c2ca98d08fcde228c2e56651..206e692876fa630a1e0ae0ba2c67d7d04d3b1c71 100644 (file)
@@ -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");
@@ -336,8 +337,9 @@ ftrace_find_call (struct btrace_function *bfun)
     {
       struct btrace_insn *last;
 
-      /* 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);
 
@@ -438,14 +440,34 @@ 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.  */
@@ -468,8 +490,8 @@ ftrace_update_function (struct btrace_function *bfun, CORE_ADDR pc)
   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.
@@ -597,13 +619,14 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
   struct btrace_thread_info *btinfo;
   struct btrace_function *begin, *end;
   struct gdbarch *gdbarch;
-  unsigned int blk;
+  unsigned int blk, ngaps;
   int level;
 
   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->blocks);
 
@@ -626,8 +649,16 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
          /* 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;
            }
 
@@ -660,6 +691,12 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
            {
              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;
            }
 
@@ -678,6 +715,7 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 
   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
@@ -808,20 +846,30 @@ btrace_teardown (struct thread_info *tp)
 /* Stitch branch trace in BTS format.  */
 
 static int
-btrace_stitch_bts (struct btrace_data_bts *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;
 
+  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.  */
-  gdb_assert (!VEC_empty (btrace_block_s, btrace->blocks));
   first_new_block = VEC_last (btrace_block_s, btrace->blocks);
   last_insn = VEC_last (btrace_insn_s, last_bfun->insn);
 
@@ -869,18 +917,25 @@ btrace_stitch_bts (struct btrace_data_bts *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.
-   BTINFO is the old branch trace until the last stop.
-   May modifx BTRACE as well as the existing trace in BTINFO.
+   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,
-                    const struct btrace_thread_info *btinfo)
+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))
@@ -892,7 +947,7 @@ btrace_stitch_trace (struct btrace_data *btrace,
       return 0;
 
     case BTRACE_FORMAT_BTS:
-      return btrace_stitch_bts (&btrace->variant.bts, btinfo);
+      return btrace_stitch_bts (&btrace->variant.bts, tp);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -946,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
        {
@@ -1009,6 +1064,7 @@ btrace_clear (struct thread_info *tp)
 
   btinfo->begin = NULL;
   btinfo->end = NULL;
+  btinfo->ngaps = 0;
 
   btrace_clear_history (btinfo);
 }
@@ -1206,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);
@@ -1222,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;
 }
 
@@ -1254,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.  */
@@ -1280,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);
 
@@ -1354,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;
@@ -1386,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);
 }
 
@@ -1397,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;
 
index 694d50478557d41eacfc47b5a3c090d78601b08f..0ddd4c1fa099c871844126b3be27cf343dcd8d40 100644 (file)
@@ -86,12 +86,25 @@ enum btrace_function_flag
   BFUN_UP_LINKS_TO_TAILCALL = (1 << 1)
 };
 
+/* Decode errors for the BTS recording format.  */
+enum btrace_bts_error
+{
+  /* The instruction trace overflowed the end of the trace block.  */
+  BDE_BTS_OVERFLOW = 1,
+
+  /* The instruction size could not be determined.  */
+  BDE_BTS_INSN_SIZE
+};
+
 /* A branch trace function segment.
 
    This represents a function segment in a branch trace, i.e. a consecutive
    number of instructions belonging to the same function.
 
-   We do not allow function segments without any instructions.  */
+   In case of decode errors, we add an empty function segment to indicate
+   the gap in the trace.
+
+   We do not allow function segments without instructions otherwise.  */
 struct btrace_function
 {
   /* The full and minimal symbol for the function.  Both may be NULL.  */
@@ -110,14 +123,23 @@ struct btrace_function
   struct btrace_function *up;
 
   /* The instructions in this function segment.
-     The instruction vector will never be empty.  */
+     The instruction vector will be empty if the function segment
+     represents a decode error.  */
   VEC (btrace_insn_s) *insn;
 
+  /* The error code of a decode error that led to a gap.
+     Must be zero unless INSN is empty; non-zero otherwise.  */
+  int errcode;
+
   /* The instruction number offset for the first instruction in this
-     function segment.  */
+     function segment.
+     If INSN is empty this is the insn_offset of the succeding function
+     segment in control-flow order.  */
   unsigned int insn_offset;
 
-  /* The function number in control-flow order.  */
+  /* The function number in control-flow order.
+     If INSN is empty indicating a gap in the trace due to a decode error,
+     we still count the gap as a function.  */
   unsigned int number;
 
   /* The function level in a back trace across the entire branch trace.
@@ -223,6 +245,9 @@ struct btrace_thread_info
      becomes zero.  */
   int level;
 
+  /* The number of gaps in the trace.  */
+  unsigned int ngaps;
+
   /* A bit-vector of btrace_thread_flag.  */
   enum btrace_thread_flag flags;
 
@@ -232,7 +257,9 @@ struct btrace_thread_info
   /* The function call history iterator.  */
   struct btrace_call_history *call_history;
 
-  /* The current replay position.  NULL if not replaying.  */
+  /* The current replay position.  NULL if not replaying.
+     Gaps are skipped during replay, so REPLAY always points to a valid
+     instruction.  */
   struct btrace_insn_iterator *replay;
 };
 
@@ -270,7 +297,8 @@ extern void parse_xml_btrace (struct btrace_data *data, const char *xml);
 extern void parse_xml_btrace_conf (struct btrace_config *conf, const char *xml);
 
 /* Dereference a branch trace instruction iterator.  Return a pointer to the
-   instruction the iterator points to.  */
+   instruction the iterator points to.
+   May return NULL if the iterator points to a gap in the trace.  */
 extern const struct btrace_insn *
   btrace_insn_get (const struct btrace_insn_iterator *);
 
index 16bba88bf5363088d922c26d797b380c62b6cbfd..102e0ebe94ed1e68b5b1084d48efaf2319dfb5f9 100644 (file)
@@ -366,7 +366,7 @@ record_btrace_info (struct target_ops *self)
   struct btrace_thread_info *btinfo;
   const struct btrace_config *conf;
   struct thread_info *tp;
-  unsigned int insns, calls;
+  unsigned int insns, calls, gaps;
 
   DEBUG ("info");
 
@@ -384,6 +384,7 @@ record_btrace_info (struct target_ops *self)
 
   insns = 0;
   calls = 0;
+  gaps = 0;
 
   if (!btrace_is_empty (tp))
     {
@@ -395,19 +396,86 @@ record_btrace_info (struct target_ops *self)
       calls = btrace_call_number (&call);
 
       btrace_insn_end (&insn, btinfo);
-      btrace_insn_prev (&insn, 1);
+
       insns = btrace_insn_number (&insn);
+      if (insns != 0)
+       {
+         /* The last instruction does not really belong to the trace.  */
+         insns -= 1;
+       }
+      else
+       {
+         unsigned int steps;
+
+         /* Skip gaps at the end.  */
+         do
+           {
+             steps = btrace_insn_prev (&insn, 1);
+             if (steps == 0)
+               break;
+
+             insns = btrace_insn_number (&insn);
+           }
+         while (insns == 0);
+       }
+
+      gaps = btinfo->ngaps;
     }
 
-  printf_unfiltered (_("Recorded %u instructions in %u functions for thread "
-                      "%d (%s).\n"), insns, calls, tp->num,
-                    target_pid_to_str (tp->ptid));
+  printf_unfiltered (_("Recorded %u instructions in %u functions (%u gaps) "
+                      "for thread %d (%s).\n"), insns, calls, gaps,
+                    tp->num, target_pid_to_str (tp->ptid));
 
   if (btrace_is_replaying (tp))
     printf_unfiltered (_("Replay in progress.  At instruction %u.\n"),
                       btrace_insn_number (btinfo->replay));
 }
 
+/* Print a decode error.  */
+
+static void
+btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
+                           enum btrace_format format)
+{
+  const char *errstr;
+  int is_error;
+
+  errstr = _("unknown");
+  is_error = 1;
+
+  switch (format)
+    {
+    default:
+      break;
+
+    case BTRACE_FORMAT_BTS:
+      switch (errcode)
+       {
+       default:
+         break;
+
+       case BDE_BTS_OVERFLOW:
+         errstr = _("instruction overflow");
+         break;
+
+       case BDE_BTS_INSN_SIZE:
+         errstr = _("unknown instruction");
+         break;
+       }
+      break;
+    }
+
+  ui_out_text (uiout, _("["));
+  if (is_error)
+    {
+      ui_out_text (uiout, _("decode error ("));
+      ui_out_field_int (uiout, "errcode", errcode);
+      ui_out_text (uiout, _("): "));
+    }
+  ui_out_text (uiout, errstr);
+  ui_out_text (uiout, _("]\n"));
+}
+
 /* Print an unsigned int.  */
 
 static void
@@ -420,6 +488,7 @@ ui_out_field_uint (struct ui_out *uiout, const char *fld, unsigned int val)
 
 static void
 btrace_insn_history (struct ui_out *uiout,
+                    const struct btrace_thread_info *btinfo,
                     const struct btrace_insn_iterator *begin,
                     const struct btrace_insn_iterator *end, int flags)
 {
@@ -437,13 +506,30 @@ btrace_insn_history (struct ui_out *uiout,
 
       insn = btrace_insn_get (&it);
 
-      /* Print the instruction index.  */
-      ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
-      ui_out_text (uiout, "\t");
+      /* A NULL instruction indicates a gap in the trace.  */
+      if (insn == NULL)
+       {
+         const struct btrace_config *conf;
+
+         conf = btrace_conf (btinfo);
 
-      /* Disassembly with '/m' flag may not produce the expected result.
-        See PR gdb/11833.  */
-      gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc, insn->pc + 1);
+         /* We have trace so we must have a configuration.  */
+         gdb_assert (conf != NULL);
+
+         btrace_ui_out_decode_error (uiout, it.function->errcode,
+                                     conf->format);
+       }
+      else
+       {
+         /* Print the instruction index.  */
+         ui_out_field_uint (uiout, "index", btrace_insn_number (&it));
+         ui_out_text (uiout, "\t");
+
+         /* Disassembly with '/m' flag may not produce the expected result.
+            See PR gdb/11833.  */
+         gdb_disassembly (gdbarch, uiout, NULL, flags, 1, insn->pc,
+                          insn->pc + 1);
+       }
     }
 }
 
@@ -520,7 +606,7 @@ record_btrace_insn_history (struct target_ops *self, int size, int flags)
     }
 
   if (covered > 0)
-    btrace_insn_history (uiout, &begin, &end, flags);
+    btrace_insn_history (uiout, btinfo, &begin, &end, flags);
   else
     {
       if (size < 0)
@@ -580,7 +666,7 @@ record_btrace_insn_history_range (struct target_ops *self,
       btrace_insn_next (&end, 1);
     }
 
-  btrace_insn_history (uiout, &begin, &end, flags);
+  btrace_insn_history (uiout, btinfo, &begin, &end, flags);
   btrace_set_insn_history (btinfo, &begin, &end);
 
   do_cleanups (uiout_cleanup);
@@ -721,6 +807,21 @@ btrace_call_history (struct ui_out *uiout,
       ui_out_field_uint (uiout, "index", bfun->number);
       ui_out_text (uiout, "\t");
 
+      /* Indicate gaps in the trace.  */
+      if (bfun->errcode != 0)
+       {
+         const struct btrace_config *conf;
+
+         conf = btrace_conf (btinfo);
+
+         /* We have trace so we must have a configuration.  */
+         gdb_assert (conf != NULL);
+
+         btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format);
+
+         continue;
+       }
+
       if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
        {
          int level = bfun->level + btinfo->level, i;
@@ -1534,6 +1635,16 @@ record_btrace_start_replaying (struct thread_info *tp)
       replay = xmalloc (sizeof (*replay));
       btrace_insn_end (replay, btinfo);
 
+      /* Skip gaps at the end of the trace.  */
+      while (btrace_insn_get (replay) == NULL)
+       {
+         unsigned int steps;
+
+         steps = btrace_insn_prev (replay, 1);
+         if (steps == 0)
+           error (_("No trace."));
+       }
+
       /* We're not replaying, yet.  */
       gdb_assert (btinfo->replay == NULL);
       btinfo->replay = replay;
@@ -1729,9 +1840,17 @@ record_btrace_step_thread (struct thread_info *tp)
       if (replay == NULL)
        return btrace_step_no_history ();
 
-      /* We are always able to step at least once.  */
-      steps = btrace_insn_next (replay, 1);
-      gdb_assert (steps == 1);
+      /* Skip gaps during replay.  */
+      do
+       {
+         steps = btrace_insn_next (replay, 1);
+         if (steps == 0)
+           {
+             record_btrace_stop_replaying (tp);
+             return btrace_step_no_history ();
+           }
+       }
+      while (btrace_insn_get (replay) == NULL);
 
       /* Determine the end of the instruction trace.  */
       btrace_insn_end (&end, btinfo);
@@ -1747,10 +1866,16 @@ record_btrace_step_thread (struct thread_info *tp)
       if (replay == NULL)
        replay = record_btrace_start_replaying (tp);
 
-      /* If we can't step any further, we reached the end of the history.  */
-      steps = btrace_insn_prev (replay, 1);
-      if (steps == 0)
-       return btrace_step_no_history ();
+      /* If we can't step any further, we reached the end of the history.
+        Skip gaps during replay.  */
+      do
+       {
+         steps = btrace_insn_prev (replay, 1);
+         if (steps == 0)
+           return btrace_step_no_history ();
+
+       }
+      while (btrace_insn_get (replay) == NULL);
 
       return btrace_step_stopped ();
 
@@ -1769,9 +1894,19 @@ record_btrace_step_thread (struct thread_info *tp)
        {
          const struct btrace_insn *insn;
 
-         /* We are always able to step at least once.  */
-         steps = btrace_insn_next (replay, 1);
-         gdb_assert (steps == 1);
+         /* Skip gaps during replay.  */
+         do
+           {
+             steps = btrace_insn_next (replay, 1);
+             if (steps == 0)
+               {
+                 record_btrace_stop_replaying (tp);
+                 return btrace_step_no_history ();
+               }
+
+             insn = btrace_insn_get (replay);
+           }
+         while (insn == NULL);
 
          /* We stop replaying if we reached the end of the trace.  */
          if (btrace_insn_cmp (replay, &end) == 0)
@@ -1780,9 +1915,6 @@ record_btrace_step_thread (struct thread_info *tp)
              return btrace_step_no_history ();
            }
 
-         insn = btrace_insn_get (replay);
-         gdb_assert (insn);
-
          DEBUG ("stepping %d (%s) ... %s", tp->num,
                 target_pid_to_str (tp->ptid),
                 core_addr_to_string_nz (insn->pc));
@@ -1803,13 +1935,17 @@ record_btrace_step_thread (struct thread_info *tp)
        {
          const struct btrace_insn *insn;
 
-         /* If we can't step any further, we're done.  */
-         steps = btrace_insn_prev (replay, 1);
-         if (steps == 0)
-           return btrace_step_no_history ();
+         /* If we can't step any further, we reached the end of the history.
+            Skip gaps during replay.  */
+         do
+           {
+             steps = btrace_insn_prev (replay, 1);
+             if (steps == 0)
+               return btrace_step_no_history ();
 
-         insn = btrace_insn_get (replay);
-         gdb_assert (insn);
+             insn = btrace_insn_get (replay);
+           }
+         while (insn == NULL);
 
          DEBUG ("reverse-stepping %d (%s) ... %s", tp->num,
                 target_pid_to_str (tp->ptid),
index f4c5ed0d9890f77565d0fd44d7b76e2a419e2ff6..4df5ba9f4143720a486d311c25a9f1b0f488d24c 100644 (file)
@@ -1,3 +1,16 @@
+2015-02-09  Markus Metzger  <markus.t.metzger@intel.com>
+
+       * gdb.btrace/buffer-size.exp: Update "info record" output.
+       * gdb.btrace/delta.exp: Update "info record" output.
+       * gdb.btrace/enable.exp: Update "info record" output.
+       * gdb.btrace/finish.exp: Update "info record" output.
+       * gdb.btrace/instruction_history.exp: Update "info record" output.
+       * gdb.btrace/next.exp: Update "info record" output.
+       * gdb.btrace/nexti.exp: Update "info record" output.
+       * gdb.btrace/step.exp: Update "info record" output.
+       * gdb.btrace/stepi.exp: Update "info record" output.
+       * gdb.btrace/nohist.exp: Update "info record" output.
+
 2015-02-09  Markus Metzger  <markus.t.metzger@intel.com>
 
        * gdb.btrace/buffer-size: New.
index 0381cc231fccbe62f5f55824675d7d19d15e849b..1f10668bf6ee5f105043920ffff42ac4c6071df6 100644 (file)
@@ -39,7 +39,7 @@ gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: Branch Trace Store\." \
   "Buffer size: 4kB\." \
-  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "info record with small bts buffer"
 gdb_test "record stop" ".*" "stop recording with small bts buffer"
 
@@ -52,6 +52,6 @@ gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: Branch Trace Store\." \
   "Buffer size: .*\." \
-  "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+  "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "info record with unlimited bts buffer"
 gdb_test "record stop" ".*" "stop recording with unlimited bts buffer"
index 59959bcd4ad0b7953e8230079c22861035b2b624..38b6e268c6f934b705efcd21594ccf3c747a39b5 100644 (file)
@@ -40,7 +40,7 @@ with_test_prefix "no trace" {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 0 instructions in 0 functions for .*" \
+    "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history" "No trace\."
   gdb_test "record function-call-history" "No trace\."
@@ -53,7 +53,7 @@ proc check_trace {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 1 instructions in 1 functions for .*" \
+    "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
     ] "\r\n"]
   gdb_test "record instruction-history /f 1" \
     "1\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tmov *\\\$0x0,%eax\r"
@@ -74,7 +74,7 @@ gdb_test "reverse-stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 1 instructions in 1 functions for .*" \
+  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
   "Replay in progress\.  At instruction 1\." \
   ] "\r\n"] "reverse-stepi"
 
@@ -83,5 +83,5 @@ gdb_test "stepi"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 1 instructions in 1 functions for .*" \
+  "Recorded 1 instructions in 1 functions \\\(0 gaps\\\) for .*" \
   ] "\r\n"] "and back"
index 1122884ced09513635e521c30bcbd043c75e4c25..d447bd9a7de3060cafb30b66618e0a0af68dfc78 100644 (file)
@@ -58,7 +58,7 @@ gdb_test "record full" "The process is already being recorded\\.  Use \"record s
 # no trace recorded yet
 gdb_test "info record" "Active record target: record-btrace\r
 .*\r
-Recorded 0 instructions in 0 functions for thread 1.*\\." "info record without trace"
+Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for thread 1.*\\." "info record without trace"
 
 # stop btrace record
 gdb_test "record stop" "Process record is stopped and all execution logs are deleted\\." "record stop"
index 3857c100b706ee107f7face54a7b0c7976df4763..6881e3bd6ca04a01e7ef04fe24d9c24a189ee0e7 100644 (file)
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
index 63d902b6d17c267e84a96c061a76ade4ec60604f..a7b57e53f977c87c72ce83f4b4ee161a466ae511 100644 (file)
@@ -50,7 +50,7 @@ gdb_continue_to_breakpoint "cont to $bp_location" ".*$srcfile2:$bp_location.*"
 set traced {}
 set testname "determine number of recorded instructions"
 gdb_test_multiple "info record" $testname {
-    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions for thread 1 .*\\.\r\n$gdb_prompt $" {
+    -re "Active record target: record-btrace\r\n.*\r\nRecorded \(\[0-9\]*\) instructions in \(\[0-9\]*\) functions \\\(0 gaps\\\) for thread 1 .*\\.\r\n$gdb_prompt $" {
         set traced $expect_out(1,string)
         pass $testname
     }
index 88bd8afa5893ee3771aedf92ce8cbd8211f0e1dd..3d2fa101075b9a3bc2d008d624e8eb889964e2ee 100644 (file)
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -57,7 +57,7 @@ gdb_test "next" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "next back"
 
 # let's go somewhere where we can step some more
index 76ca0a61c15986e31d645a648684e1fff41b64d7..911ad868b7e2751ee01b50967eaf6cba7c69d093 100644 (file)
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -57,7 +57,7 @@ gdb_test "nexti" ".*main\.3.*" "next, 1.5"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "nexti back"
 
 # let's go somewhere where we can step some more
index f53870bc94bfea24e44d7fc419c7987ad1fc6bb7..f26725002d8c45affb3d71f7da247fa58f99dcc8 100644 (file)
@@ -34,7 +34,7 @@ proc check_not_replaying {} {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-       "Recorded 0 instructions in 0 functions for \[^\\\r\\\n\]*" \
+       "Recorded 0 instructions in 0 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
     ] "\r\n"]
 }
 
index e3febe1cc5f0d880861bdb17519c26c76f382010..22aded819464b53b6647c346eced5262fd1a4cc6 100644 (file)
@@ -38,7 +38,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -87,5 +87,5 @@ gdb_test "step" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "step to live"
index 0276f722b369d4010ed9c98c28019b54e2570375..a663f87fbc689b4b3961cf784526a6efd79fca5e 100644 (file)
@@ -36,7 +36,7 @@ proc check_replay_at { insn } {
   gdb_test "info record" [join [list \
     "Active record target: record-btrace" \
     "Recording format: .*" \
-    "Recorded 40 instructions in 16 functions for .*" \
+    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "\r\n"]
 }
@@ -61,7 +61,7 @@ gdb_test "stepi" ".*main\.3.*"
 gdb_test "info record" [join [list \
   "Active record target: record-btrace" \
   "Recording format: .*" \
-  "Recorded 40 instructions in 16 functions for \[^\\\r\\\n\]*" \
+  "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] "\r\n"] "stepi to live"
 
 # let's step from a goto position somewhere in the middle