[PATCH v2 4/6] btrace: Add support for interrupt events.

Willgerodt, Felix felix.willgerodt@intel.com
Tue Sep 24 07:40:16 GMT 2024


> -----Original Message-----
> From: Metzger, Markus T <markus.t.metzger@intel.com>
> Sent: Montag, 23. September 2024 15:45
> To: Willgerodt, Felix <felix.willgerodt@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: RE: [PATCH v2 4/6] btrace: Add support for interrupt events.
> 
> Hello Felix,
> 
> > /* Update the current function segment at the end of the trace in BTINFO
> with
> >    respect to the instruction at PC.  This may create new function segments.
> >-   Return the chronologically latest function segment, never NULL.  */
> >+   Return the chronologically latest function segment, never NULL.
> >+   If VALID_PC is false, skip analyzing whether the function has changed.  */
> >
> > static struct btrace_function *
> >-ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
> >+ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc,
> >+			const bool valid_pc = true)
> 
> I would go without the default.  If you want to simplify calls, we could add two
> overloads:
> 
> ftrace_update_function (btrace_thread_info *)
> ftrace_update_function (btrace_thread_info *, CORE_ADDR)
> 
> This would make it clearer that one is w/o a PC and one is with.
> 
> Or we make the PC argument std::optional<CORE_ADDR>.  This probably fits
> best how we process events.
> 
> Regards,
> Markus.

I also had thought about optional previously and prefer it as well.
Here are the new changes:


diff --git a/gdb/btrace.c b/gdb/btrace.c
index 649e0ad59d7..3cf386d5a75 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -547,31 +547,39 @@ ftrace_new_gap (struct btrace_thread_info *btinfo, int errcode,
    Return the chronologically latest function segment, never NULL.  */
 
 static struct btrace_function *
-ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
+ftrace_update_function (struct btrace_thread_info *btinfo,
+			std::optional<CORE_ADDR> pc)
 {
-  struct minimal_symbol *mfun;
-  struct symbol *fun;
-  struct btrace_function *bfun;
+  struct minimal_symbol *mfun = nullptr;
+  struct symbol *fun = nullptr;
 
   /* Try to determine the function we're in.  We use both types of symbols
      to avoid surprises when we sometimes get a full symbol and sometimes
      only a minimal symbol.  */
-  fun = find_pc_function (pc);
-  bound_minimal_symbol bmfun = lookup_minimal_symbol_by_pc (pc);
-  mfun = bmfun.minsym;
+  if (pc.has_value ())
+    {
+      fun = find_pc_function (*pc);
+      bound_minimal_symbol bmfun = lookup_minimal_symbol_by_pc (*pc);
+      mfun = bmfun.minsym;
 
-  if (fun == NULL && mfun == NULL)
-    DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
+      if (fun == nullptr && mfun == nullptr)
+	DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (*pc));
+    }
 
   /* If we didn't have a function, we create one.  */
   if (btinfo->functions.empty ())
     return ftrace_new_function (btinfo, mfun, fun);
 
   /* If we had a gap before, we create a function.  */
-  bfun = &btinfo->functions.back ();
+  btrace_function *bfun = &btinfo->functions.back ();
   if (bfun->errcode != 0)
     return ftrace_new_function (btinfo, mfun, fun);
 
+  /* If there is no valid PC, which can happen for events with a
+     suppressed IP, we can't do more than return the last bfun.  */
+  if (!pc.has_value ())
+    return bfun;
+
   /* Check the last instruction, if we have one.
      We do this check first, since it allows us to fill in the call stack
      links in addition to the normal flow links.  */
@@ -605,7 +613,7 @@ ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
 
 	case BTRACE_INSN_CALL:
 	  /* Ignore calls to the next instruction.  They are used for PIC.  */
-	  if (last->pc + last->size == pc)
+	  if (last->pc + last->size == *pc)
 	    break;
 
 	  return ftrace_new_call (btinfo, mfun, fun);
@@ -614,10 +622,10 @@ ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
 	  {
 	    CORE_ADDR start;
 
-	    start = get_pc_function_start (pc);
+	    start = get_pc_function_start (*pc);
 
 	    /* A jump to the start of a function is (typically) a tail call.  */
-	    if (start == pc)
+	    if (start == *pc)
 	      return ftrace_new_tailcall (btinfo, mfun, fun);
 
 	    /* Some versions of _Unwind_RaiseException use an indirect
@@ -642,6 +650,18 @@ ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
 
 	    break;
 	  }
+
+	case BTRACE_INSN_AUX:
+	  /* An aux insn couldn't have switched the function.  But the
+	     segment might not have had a symbol name resolved yet, as events
+	     might not have an IP.  Use the current IP in that case and update
+	     the name.  */
+	  if (bfun->sym == nullptr && bfun->msym == nullptr)
+	    {
+	      bfun->sym = fun;
+	      bfun->msym = mfun;
+	    }
+	  break;
 	}
     }
 
@@ -668,6 +688,8 @@ ftrace_update_insns (struct btrace_function *bfun, const btrace_insn &insn)
 
   if (insn.iclass == BTRACE_INSN_AUX)
     bfun->flags |= BFUN_CONTAINS_AUX;
+  else
+    bfun->flags |= BFUN_CONTAINS_NON_AUX;
 
   if (record_debug > 1)
     ftrace_debug (bfun, "update insn");
@@ -1101,7 +1123,8 @@ btrace_compute_ftrace_bts (struct thread_info *tp,
 	      break;
 	    }
 
-	  bfun = ftrace_update_function (btinfo, pc);
+	  bfun = ftrace_update_function (btinfo,
+					 std::make_optional<CORE_ADDR> (pc));
 
 	  /* Maintain the function level offset.
 	     For all but the last block, we do it here.  */
@@ -1211,10 +1234,10 @@ pt_btrace_insn (const struct pt_insn &insn)
 
 static void
 handle_pt_aux_insn (btrace_thread_info *btinfo, std::string &aux_str,
-		    CORE_ADDR ip)
+		    std::optional<CORE_ADDR> pc)
 {
   btinfo->aux_data.emplace_back (std::move (aux_str));
-  struct btrace_function *bfun = ftrace_update_function (btinfo, ip);
+  struct btrace_function *bfun = ftrace_update_function (btinfo, pc);
 
   btrace_insn insn {{btinfo->aux_data.size () - 1}, 0,
 		    BTRACE_INSN_AUX, 0};
@@ -1222,8 +1245,47 @@ handle_pt_aux_insn (btrace_thread_info *btinfo, std::string &aux_str,
   ftrace_update_insns (bfun, insn);
 }
 
+/* Check if the recording contains real instructions and not only auxiliary
+   instructions since the last gap (or since the beginning).  */
+
+static bool
+ftrace_contains_non_aux_since_last_gap (const btrace_thread_info *btinfo)
+{
+  const std::vector<btrace_function> &functions = btinfo->functions;
+
+  std::vector<btrace_function>::const_reverse_iterator rit;
+  for (rit = functions.crbegin (); rit != functions.crend (); ++rit)
+    {
+      if (rit->errcode != 0)
+	return false;
+
+      if ((rit->flags & BFUN_CONTAINS_NON_AUX) != 0)
+	return true;
+    }
+
+  return false;
+}
 #endif /* defined (HAVE_PT_INSN_EVENT) */
 
+#if (LIBIPT_VERSION >= 0x201)
+/* Translate an interrupt vector to a mnemonic string as defined for x86.
+   Returns nullptr if there is none.  */
+
+static const char *
+decode_interrupt_vector (const uint8_t vector)
+{
+  static const char *mnemonic[]
+    = { "#de", "#db", nullptr, "#bp", "#of", "#br", "#ud", "#nm",
+	"#df", "#mf", "#ts", "#np", "#ss", "#gp", "#pf", nullptr,
+	"#mf", "#ac", "#mc", "#xm", "#ve", "#cp" };
+
+  if (vector < (sizeof (mnemonic) / sizeof (mnemonic[0])))
+    return mnemonic[vector];
+
+  return nullptr;
+}
+#endif /* defined (LIBIPT_VERSION >= 0x201) */
+
 /* Handle instruction decode events (libipt-v2).  */
 
 static int
@@ -1236,6 +1298,7 @@ handle_pt_insn_events (struct btrace_thread_info *btinfo,
     {
       struct pt_event event;
       uint64_t offset;
+      std::optional<CORE_ADDR> pc;
 
       status = pt_insn_event (decoder, &event, sizeof (event));
       if (status < 0)
@@ -1251,8 +1314,13 @@ handle_pt_insn_events (struct btrace_thread_info *btinfo,
 	    if (event.status_update != 0)
 	      break;
 
+	    /* Only create a new gap if there are non-aux instructions in
+	       the trace since the last gap.  We could be at the beginning
+	       of the recording and could already have handled one or more
+	       events, like ptev_iret, that created aux insns.  In that
+	       case we don't want to create a gap or print a warning.  */
 	    if (event.variant.enabled.resumed == 0
-		&& !btinfo->functions.empty ())
+		&& ftrace_contains_non_aux_since_last_gap (btinfo))
 	      {
 		struct btrace_function *bfun
 		  = ftrace_new_gap (btinfo, BDE_PT_NON_CONTIGUOUS, gaps);
@@ -1282,7 +1350,6 @@ handle_pt_insn_events (struct btrace_thread_info *btinfo,
 #if defined (HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE)
 	case ptev_ptwrite:
 	  {
-	    uint64_t pc = 0;
 	    std::optional<std::string> ptw_string;
 
 	    /* Lookup the PC if available.  The event often doesn't provide
@@ -1314,13 +1381,16 @@ handle_pt_insn_events (struct btrace_thread_info *btinfo,
 		  }
 	      }
 
-	    if (pc == 0)
-	      warning (_("Failed to determine the PC for ptwrite."));
+	    if (!pc.has_value ())
+	      {
+		warning (_("Failed to determine the PC for ptwrite."));
+		pc = 0;
+	      }
 
 	    if (btinfo->ptw_callback_fun != nullptr)
 	      ptw_string
 		= btinfo->ptw_callback_fun (event.variant.ptwrite.payload,
-					    pc, btinfo->ptw_context);
+					    *pc, btinfo->ptw_context);
 
 	    if (ptw_string.has_value () && (*ptw_string).empty ())
 	      continue;
@@ -1333,6 +1403,34 @@ handle_pt_insn_events (struct btrace_thread_info *btinfo,
 	    break;
 	  }
 #endif /* defined (HAVE_STRUCT_PT_EVENT_VARIANT_PTWRITE) */
+
+#if (LIBIPT_VERSION >= 0x201)
+	case ptev_interrupt:
+	  {
+	    std::string aux_string = std::string (_("interrupt: vector = "))
+	      + hex_string (event.variant.interrupt.vector);
+
+	    const char *decoded
+	      = decode_interrupt_vector (event.variant.interrupt.vector);
+	    if (decoded != nullptr)
+	      aux_string += std::string (" (") + decoded + ")";
+
+	    if (event.variant.interrupt.has_cr2 != 0)
+	      {
+		aux_string += std::string (", cr2 = ")
+		  + hex_string (event.variant.interrupt.cr2);
+	      }
+
+	    if (event.ip_suppressed == 0)
+	      {
+		pc = event.variant.interrupt.ip;
+		aux_string += std::string (", ip = ") + hex_string (*pc);
+	      }
+
+	    handle_pt_aux_insn (btinfo, aux_string, pc);
+	    break;
+	  }
+#endif /* defined (LIBIPT_VERSION >= 0x201) */
 	}
     }
 #endif /* defined (HAVE_PT_INSN_EVENT) */
@@ -1428,7 +1526,9 @@ ftrace_add_pt (struct btrace_thread_info *btinfo,
 	  /* Handle events indicated by flags in INSN.  */
 	  handle_pt_insn_event_flags (btinfo, decoder, insn, gaps);
 
-	  bfun = ftrace_update_function (btinfo, insn.ip);
+	  bfun
+	    = ftrace_update_function (btinfo,
+				      std::make_optional<CORE_ADDR> (insn.ip));
 
 	  /* Maintain the function level offset.  */
 	  *plevel = std::min (*plevel, bfun->level);



Please let me know if this is ok. I can also send a v3.

Thanks,
Felix

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


More information about the Gdb-patches mailing list