This revision was automatically updated to reflect the committed changes.
Closed by commit rG059f39d2f445: [trace][intel pt] Support events (authored by 
Walter Erquinigo <wall...@fb.com>).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D123982/new/

https://reviews.llvm.org/D123982

Files:
  lldb/include/lldb/Target/TraceCursor.h
  lldb/include/lldb/Target/TraceInstructionDumper.h
  lldb/include/lldb/lldb-enumerations.h
  lldb/source/Commands/CommandObjectThread.cpp
  lldb/source/Commands/Options.td
  lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
  lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
  lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
  lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
  lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
  lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
  lldb/source/Target/TraceCursor.cpp
  lldb/source/Target/TraceInstructionDumper.cpp
  lldb/test/API/commands/trace/TestTraceDumpInfo.py
  lldb/test/API/commands/trace/TestTraceEvents.py
  lldb/test/API/commands/trace/TestTraceLoad.py

Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -40,12 +40,17 @@
 
   Memory usage:
     Raw trace size: 4 KiB
-    Total approximate memory usage (excluding raw trace): 0.27 KiB
-    Average memory usage per instruction (excluding raw trace): 13.00 bytes
+    Total approximate memory usage (excluding raw trace): 1.27 KiB
+    Average memory usage per instruction (excluding raw trace): 61.76 bytes
 
   Timing:
     Decoding instructions: ''', '''s
 
+  Events:
+    Number of instructions with events: 1
+    Number of individual events: 1
+      paused: 1
+
   Errors:
     Number of TSC decoding errors: 0'''])
 
Index: lldb/test/API/commands/trace/TestTraceEvents.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/TestTraceEvents.py
@@ -0,0 +1,82 @@
+import lldb
+from intelpt_testcase import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceEvents(TraceIntelPTTestCaseBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @testSBAPIAndCommands
+    def testPauseEvents(self):
+      '''
+        Everytime the target stops running on the CPU, a 'disabled' event will
+        be emitted, which is represented by the TraceCursor API as a 'paused'
+        event.
+      '''
+      self.expect("target create " +
+            os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "a.out"))
+      self.expect("b 12")
+      self.expect("r")
+      self.traceStartThread()
+      self.expect("n")
+      self.expect("n")
+      self.expect("si")
+      self.expect("si")
+      self.expect("si")
+      # We ensure that the paused events are printed correctly forward
+      self.expect("thread trace dump instructions -e -f",
+        patterns=[f'''thread #1: tid = .*
+  a.out`main \+ 23 at main.cpp:12
+    0: {ADDRESS_REGEX}    movl .*
+  \[paused\]
+    1: {ADDRESS_REGEX}    addl .*
+    2: {ADDRESS_REGEX}    movl .*
+  \[paused\]
+  a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4
+    3: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 41 \[inlined\] inline_function\(\) \+ 7 at main.cpp:5
+    4: {ADDRESS_REGEX}    movl .*
+    5: {ADDRESS_REGEX}    addl .*
+    6: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6
+    7: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 55 at main.cpp:14
+    8: {ADDRESS_REGEX}    movl .*
+    9: {ADDRESS_REGEX}    addl .*
+    10: {ADDRESS_REGEX}    movl .*
+  \[paused\]
+  a.out`main \+ 63 at main.cpp:16
+    11: {ADDRESS_REGEX}    callq  .* ; symbol stub for: foo\(\)
+  \[paused\]
+  a.out`symbol stub for: foo\(\)
+    12: {ADDRESS_REGEX}    jmpq'''])
+
+      # We ensure that the paused events are printed correctly backward
+      self.expect("thread trace dump instructions -e --id 12",
+        patterns=[f'''thread #1: tid = .*
+  a.out`symbol stub for: foo\(\)
+    12: {ADDRESS_REGEX}    jmpq .*
+  \[paused\]
+  a.out`main \+ 63 at main.cpp:16
+    11: {ADDRESS_REGEX}    callq  .* ; symbol stub for: foo\(\)
+  \[paused\]
+  a.out`main \+ 60 at main.cpp:14
+    10: {ADDRESS_REGEX}    movl .*
+    9: {ADDRESS_REGEX}    addl .*
+    8: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6
+    7: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 49 \[inlined\] inline_function\(\) \+ 15 at main.cpp:5
+    6: {ADDRESS_REGEX}    movl .*
+    5: {ADDRESS_REGEX}    addl .*
+    4: {ADDRESS_REGEX}    movl .*
+  a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4
+    3: {ADDRESS_REGEX}    movl .*
+  \[paused\]
+  a.out`main \+ 31 at main.cpp:12
+    2: {ADDRESS_REGEX}    movl .*
+    1: {ADDRESS_REGEX}    addl .*
+  \[paused\]
+    0: {ADDRESS_REGEX}    movl .*'''])
Index: lldb/test/API/commands/trace/TestTraceDumpInfo.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceDumpInfo.py
+++ lldb/test/API/commands/trace/TestTraceDumpInfo.py
@@ -42,12 +42,17 @@
 
   Memory usage:
     Raw trace size: 4 KiB
-    Total approximate memory usage (excluding raw trace): 0.27 KiB
-    Average memory usage per instruction (excluding raw trace): 13.00 bytes
+    Total approximate memory usage (excluding raw trace): 1.27 KiB
+    Average memory usage per instruction (excluding raw trace): 61.76 bytes
 
   Timing:
     Decoding instructions: ''', '''s
 
+  Events:
+    Number of instructions with events: 1
+    Number of individual events: 1
+      paused: 1
+
   Errors:
     Number of TSC decoding errors: 0'''],
             patterns=["Decoding instructions: \d.\d\ds"])
Index: lldb/source/Target/TraceInstructionDumper.cpp
===================================================================
--- lldb/source/Target/TraceInstructionDumper.cpp
+++ lldb/source/Target/TraceInstructionDumper.cpp
@@ -172,6 +172,16 @@
 
 bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; }
 
+void TraceInstructionDumper::PrintEvents() {
+  if (!m_options.show_events)
+    return;
+
+  trace_event_utils::ForEachEvent(
+      m_cursor_up->GetEvents(), [&](TraceEvents event) {
+        m_s.Format("  [{0}]\n", trace_event_utils::EventToDisplayString(event));
+      });
+}
+
 Optional<lldb::tid_t> TraceInstructionDumper::DumpInstructions(size_t count) {
   ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
   if (!thread_sp) {
@@ -259,6 +269,11 @@
       break;
     }
     last_id = m_cursor_up->GetId();
+    if (m_options.forwards) {
+      // When moving forwards, we first print the event before printing
+      // the actual instruction.
+      PrintEvents();
+    }
 
     if (const char *err = m_cursor_up->GetError()) {
       if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
@@ -297,6 +312,13 @@
     }
 
     m_s.Printf("\n");
+
+    if (!m_options.forwards) {
+      // If we move backwards, we print the events after printing
+      // the actual instruction so that reading chronologically
+      // makes sense.
+      PrintEvents();
+    }
     TryMoveOneStep();
   }
   return last_id;
Index: lldb/source/Target/TraceCursor.cpp
===================================================================
--- lldb/source/Target/TraceCursor.cpp
+++ lldb/source/Target/TraceCursor.cpp
@@ -33,3 +33,21 @@
 void TraceCursor::SetForwards(bool forwards) { m_forwards = forwards; }
 
 bool TraceCursor::IsForwards() const { return m_forwards; }
+
+const char *trace_event_utils::EventToDisplayString(lldb::TraceEvents event) {
+  switch (event) {
+  case lldb::eTraceEventPaused:
+    return "paused";
+  }
+  return nullptr;
+}
+
+void trace_event_utils::ForEachEvent(
+    lldb::TraceEvents events,
+    std::function<void(lldb::TraceEvents event)> callback) {
+  while (events) {
+    TraceEvents event = static_cast<TraceEvents>(events & ~(events - 1));
+    callback(event);
+    events &= ~event;
+  }
+}
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -142,11 +142,25 @@
         s.Format("    {0}: {1:2}s\n", name, duration.count() / 1000.0);
       });
 
+  const DecodedThread::EventsStats &events_stats =
+      decoded_trace_sp->GetEventsStats();
+  s << "\n  Events:\n";
+  s.Format("    Number of instructions with events: {0}\n",
+           events_stats.total_instructions_with_events);
+  s.Format("    Number of individual events: {0}\n", events_stats.total_count);
+  for (const auto &event_to_count : events_stats.events_counts) {
+    s.Format("      {0}: {1}\n",
+             trace_event_utils::EventToDisplayString(event_to_count.first),
+             event_to_count.second);
+  }
+
   s << "\n  Errors:\n";
-  const DecodedThread::LibiptErrors &tsc_errors =
-      decoded_trace_sp->GetTscErrors();
-  s.Format("    Number of TSC decoding errors: {0}\n", tsc_errors.total_count);
-  for (const auto &error_message_to_count : tsc_errors.libipt_errors) {
+  const DecodedThread::LibiptErrorsStats &tsc_errors_stats =
+      decoded_trace_sp->GetTscErrorsStats();
+  s.Format("    Number of TSC decoding errors: {0}\n",
+           tsc_errors_stats.total_count);
+  for (const auto &error_message_to_count :
+       tsc_errors_stats.libipt_errors_counts) {
     s.Format("      {0}: {1}\n", error_message_to_count.first,
              error_message_to_count.second);
   }
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h
@@ -30,6 +30,8 @@
 
   llvm::Optional<uint64_t> GetCounter(lldb::TraceCounter counter_type) override;
 
+  lldb::TraceEvents GetEvents() override;
+
   lldb::TraceInstructionControlFlowType
   GetInstructionControlFlowType() override;
 
Index: lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp
@@ -108,6 +108,10 @@
   }
 }
 
+lldb::TraceEvents TraceCursorIntelPT::GetEvents() {
+  return m_decoded_thread_sp->GetEvents(m_pos);
+}
+
 TraceInstructionControlFlowType
 TraceCursorIntelPT::GetInstructionControlFlowType() {
   return m_decoded_thread_sp->GetInstructionControlFlowType(m_pos);
Index: lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp
@@ -43,7 +43,7 @@
             });
 
         if (err)
-          decoded_thread_sp->AppendError(std::move(err));
+          decoded_thread_sp->SetAsFailed(std::move(err));
         return decoded_thread_sp;
       });
 }
Index: lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp
@@ -26,18 +26,6 @@
   explicit operator bool() const { return has_tsc == eLazyBoolYes; }
 };
 
-static inline bool IsLibiptError(int libipt_status) {
-  return libipt_status < 0;
-}
-
-static inline bool IsEndOfStream(int libipt_status) {
-  return libipt_status == -pte_eos;
-}
-
-static inline bool IsTscUnavailable(int libipt_status) {
-  return libipt_status == -pte_no_time;
-}
-
 /// Class that decodes a raw buffer for a single thread using the low level
 /// libipt library.
 ///
@@ -75,20 +63,33 @@
   }
 
 private:
+  /// Invoke the low level function \a pt_insn_next and store the decoded
+  /// instruction in the given \a DecodedInstruction.
+  ///
+  /// \param[out] insn
+  ///   The instruction builder where the pt_insn information will be stored.
+  ///
+  /// \return
+  ///   The status returned by pt_insn_next.
+  int DecodeNextInstruction(DecodedInstruction &insn) {
+    return pt_insn_next(&m_decoder, &insn.pt_insn, sizeof(insn.pt_insn));
+  }
+
   /// Decode all the instructions and events until an error is found or the end
   /// of the trace is reached.
   ///
   /// \param[in] status
   ///   The status that was result of synchronizing to the most recent PSB.
   void DecodeInstructionsAndEvents(int status) {
-    pt_insn insn;
-    while (ProcessPTEvents(status)) {
-      status = pt_insn_next(&m_decoder, &insn, sizeof(insn));
-      // The status returned by pt_insn_next will need to be processed by
-      // ProcessPTEvents in the next loop.
-      if (FoundErrors(status, insn.ip))
+    while (DecodedInstruction insn = ProcessPTEvents(status)) {
+      // The status returned by DecodeNextInstruction will need to be processed
+      // by ProcessPTEvents in the next loop if it is not an error.
+      if (IsLibiptError(status = DecodeNextInstruction(insn))) {
+        insn.libipt_error = status;
+        m_decoded_thread.Append(insn);
         break;
-      AppendInstruction(insn);
+      }
+      m_decoded_thread.Append(insn);
     }
   }
 
@@ -98,6 +99,8 @@
   /// Once the decoder is at that synchronization point, it can start decoding
   /// instructions.
   ///
+  /// If errors are found, they will be appended to the trace.
+  ///
   /// \return
   ///   The libipt decoder status after moving to the next PSB. Negative if
   ///   no PSB was found.
@@ -135,7 +138,9 @@
     }
 
     // We make this call to record any synchronization errors.
-    FoundErrors(status);
+    if (IsLibiptError(status))
+      m_decoded_thread.Append(DecodedInstruction(status));
+
     return status;
   }
 
@@ -143,21 +148,60 @@
   /// instruction e.g. timing events like ptev_tick, or paging events like
   /// ptev_paging.
   ///
+  /// If an error is found, it will be appended to the trace.
+  ///
+  /// \param[in] status
+  ///   The status gotten from the previous instruction decoding or PSB
+  ///   synchronization.
+  ///
   /// \return
-  ///   \b true if we could process the events, \b false if errors were found.
-  bool ProcessPTEvents(int status) {
+  ///   A \a DecodedInstruction with event, tsc and error information.
+  DecodedInstruction ProcessPTEvents(int status) {
+    DecodedInstruction insn;
     while (status & pts_event_pending) {
       pt_event event;
       status = pt_insn_event(&m_decoder, &event, sizeof(event));
-      if (IsLibiptError(status))
+      if (IsLibiptError(status)) {
+        insn.libipt_error = status;
         break;
+      }
+
+      switch (event.type) {
+      case ptev_enabled:
+        // The kernel started or resumed tracing the program.
+        break;
+      case ptev_disabled:
+        // The CPU paused tracing the program, e.g. due to ip filtering.
+      case ptev_async_disabled:
+        // The kernel or user code paused tracing the program, e.g.
+        // a breakpoint or a ioctl invocation pausing the trace, or a
+        // context switch happened.
+
+        if (m_decoded_thread.GetInstructionsCount() > 0) {
+          // A paused event before the first instruction can be safely
+          // discarded.
+          insn.events |= eTraceEventPaused;
+        }
+        break;
+      case ptev_overflow:
+        // The CPU internal buffer had an overflow error and some instructions
+        // were lost.
+        insn.libipt_error = -pte_overflow;
+        break;
+      default:
+        break;
+      }
     }
 
     // We refresh the TSC that might have changed after processing the events.
     // See
     // https://github.com/intel/libipt/blob/master/doc/man/pt_evt_next.3.md
     RefreshTscInfo();
-    return !FoundErrors(status);
+    if (m_tsc_info)
+      insn.tsc = m_tsc_info.tsc;
+    if (!insn)
+      m_decoded_thread.Append(insn);
+    return insn;
   }
 
   /// Query the decoder for the most recent TSC timestamp and update
@@ -189,39 +233,6 @@
     }
   }
 
-  /// Check if the given libipt status signals any errors. If errors were found,
-  /// they will be recorded in the decoded trace.
-  ///
-  /// \param[in] ip
-  ///     An optional ip address can be passed if the error is associated with
-  ///     the decoding of a specific instruction.
-  ///
-  /// \return
-  ///     \b true if errors were found, \b false otherwise.
-  bool FoundErrors(int status, lldb::addr_t ip = LLDB_INVALID_ADDRESS) {
-    if (!IsLibiptError(status))
-      return false;
-
-    // We signal a gap only if it's not "end of stream", as that's not a proper
-    // error.
-    if (!IsEndOfStream(status)) {
-      if (m_tsc_info) {
-        m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip),
-                                     m_tsc_info.tsc);
-      } else {
-        m_decoded_thread.AppendError(make_error<IntelPTError>(status, ip));
-      }
-    }
-    return true;
-  }
-
-  void AppendInstruction(const pt_insn &insn) {
-    if (m_tsc_info)
-      m_decoded_thread.AppendInstruction(insn, m_tsc_info.tsc);
-    else
-      m_decoded_thread.AppendInstruction(insn);
-  }
-
 private:
   pt_insn_decoder &m_decoder;
   DecodedThread &m_decoded_thread;
@@ -293,7 +304,7 @@
   Expected<PtInsnDecoderUP> decoder_up =
       CreateInstructionDecoder(decoded_thread, trace_intel_pt, buffer);
   if (!decoder_up)
-    return decoded_thread.AppendError(decoder_up.takeError());
+    return decoded_thread.SetAsFailed(decoder_up.takeError());
 
   LibiptDecoder libipt_decoder(*decoder_up.get(), decoded_thread);
   libipt_decoder.DecodeUntilEndOfTrace();
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -23,6 +23,15 @@
 namespace lldb_private {
 namespace trace_intel_pt {
 
+/// libipt status utils
+/// \{
+bool IsLibiptError(int libipt_status);
+
+bool IsEndOfStream(int libipt_status);
+
+bool IsTscUnavailable(int libipt_status);
+/// \}
+
 /// Class for representing a libipt decoding error.
 class IntelPTError : public llvm::ErrorInfo<IntelPTError> {
 public:
@@ -51,6 +60,27 @@
   lldb::addr_t m_address;
 };
 
+/// Helper struct for building an instruction or error from the decoder.
+/// It holds associated events and timing information.
+struct DecodedInstruction {
+  DecodedInstruction() {
+    pt_insn.ip = LLDB_INVALID_ADDRESS;
+    libipt_error = pte_ok;
+  }
+
+  DecodedInstruction(int libipt_error_code) : DecodedInstruction() {
+    libipt_error = libipt_error_code;
+  }
+
+  /// \return \b true if and only if this struct holds a libipt error.
+  explicit operator bool() const;
+
+  int libipt_error;
+  lldb::TraceEvents events = (lldb::TraceEvents)0;
+  llvm::Optional<uint64_t> tsc = llvm::None;
+  pt_insn pt_insn;
+};
+
 /// \class DecodedThread
 /// Class holding the instructions and function call hierarchy obtained from
 /// decoding a trace, as well as a position cursor used when reverse debugging
@@ -101,30 +131,40 @@
   };
 
   // Struct holding counts for libipts errors;
-  struct LibiptErrors {
+  struct LibiptErrorsStats {
     // libipt error -> count
-    llvm::DenseMap<const char *, int> libipt_errors;
-    int total_count = 0;
+    llvm::DenseMap<const char *, int> libipt_errors_counts;
+    size_t total_count = 0;
 
     void RecordError(int libipt_error_code);
   };
 
+  // Struct holding counts for events;
+  struct EventsStats {
+    /// A count for each individual event kind. We use an unordered map instead
+    /// of a DenseMap because DenseMap can't understand enums.
+    std::unordered_map<lldb::TraceEvents, size_t> events_counts;
+    size_t total_count = 0;
+    size_t total_instructions_with_events = 0;
+
+    void RecordEventsForInstruction(lldb::TraceEvents events);
+  };
+
   DecodedThread(lldb::ThreadSP thread_sp);
 
   /// Utility constructor that initializes the trace with a provided error.
   DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err);
 
-  /// Append a successfully decoded instruction.
-  void AppendInstruction(const pt_insn &instruction);
+  /// Append an instruction or a libipt error.
+  void Append(const DecodedInstruction &insn);
 
-  /// Append a sucessfully decoded instruction with an associated TSC timestamp.
-  void AppendInstruction(const pt_insn &instruction, uint64_t tsc);
-
-  /// Append a decoding error (i.e. an instruction that failed to be decoded).
-  void AppendError(llvm::Error &&error);
+  /// Append an error signaling that decoding completely failed.
+  void SetAsFailed(llvm::Error &&error);
 
-  /// Append a decoding error with a corresponding TSC.
-  void AppendError(llvm::Error &&error, uint64_t tsc);
+  /// Get a bitmask with the events that happened chronologically right before
+  /// the instruction pointed by the given instruction index, but after the
+  /// previous instruction.
+  lldb::TraceEvents GetEvents(int insn_index);
 
   /// Get the total number of instruction pointers from the decoded trace.
   /// This will include instructions that indicate errors (or gaps) in the
@@ -175,13 +215,19 @@
   /// Get a new cursor for the decoded thread.
   lldb::TraceCursorUP GetCursor();
 
-  /// Return the number of TSC decoding errors that happened. A TSC error
-  /// is not a fatal error and doesn't create gaps in the trace. Instead
-  /// we only keep track of them as a statistic.
+  /// Return an object with statistics of the TSC decoding errors that happened.
+  /// A TSC error is not a fatal error and doesn't create gaps in the trace.
+  /// Instead we only keep track of them as statistics.
   ///
   /// \return
-  ///   The number of TSC decoding errors.
-  const LibiptErrors &GetTscErrors() const;
+  ///   An object with the statistics of TSC decoding errors.
+  const LibiptErrorsStats &GetTscErrorsStats() const;
+
+  /// Return an object with statistics of the trace events that happened.
+  ///
+  /// \return
+  ///   The stats object of all the events.
+  const EventsStats &GetEventsStats() const;
 
   /// Record an error decoding a TSC timestamp.
   ///
@@ -198,6 +244,9 @@
   lldb::ThreadSP GetThread();
 
 private:
+  /// Append a decoding error given an llvm::Error.
+  void AppendError(llvm::Error &&error);
+
   /// Notify this class that the last added instruction or error has
   /// an associated TSC.
   void RecordTscForLastInstruction(uint64_t tsc);
@@ -219,17 +268,20 @@
   /// are sporadic and we can think of them as ranges. If TSCs are present in
   /// the trace, all instructions will have an associated TSC, including the
   /// first one. Otherwise, this map will be empty.
-  std::map<size_t, uint64_t> m_instruction_timestamps;
+  std::map<uint64_t, uint64_t> m_instruction_timestamps;
   /// This is the chronologically last TSC that has been added.
   llvm::Optional<uint64_t> m_last_tsc = llvm::None;
   // This variables stores the messages of all the error instructions in the
   // trace. It maps `instruction index -> error message`.
   llvm::DenseMap<uint64_t, std::string> m_errors;
-  /// The size in bytes of the raw buffer before decoding. It might be None if
-  /// the decoding failed.
-  llvm::Optional<size_t> m_raw_trace_size;
-  /// All occurrences of libipt errors when decoding TSCs.
-  LibiptErrors m_tsc_errors;
+  /// This variable stores the bitmask of events that happened right before
+  /// the instruction given as a key. It maps `instruction index -> events`.
+  llvm::DenseMap<uint64_t, lldb::TraceEvents> m_events;
+
+  /// Statistics of all tracing events.
+  EventsStats m_events_stats;
+  /// Statistics of libipt errors when decoding TSCs.
+  LibiptErrorsStats m_tsc_errors_stats;
   /// Total amount of time spent decoding.
   std::chrono::milliseconds m_total_decoding_time{0};
 };
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -19,6 +19,18 @@
 using namespace lldb_private::trace_intel_pt;
 using namespace llvm;
 
+bool lldb_private::trace_intel_pt::IsLibiptError(int libipt_status) {
+  return libipt_status < 0;
+}
+
+bool lldb_private::trace_intel_pt::IsEndOfStream(int libipt_status) {
+  return libipt_status == -pte_eos;
+}
+
+bool lldb_private::trace_intel_pt::IsTscUnavailable(int libipt_status) {
+  return libipt_status == -pte_no_time;
+}
+
 char IntelPTError::ID;
 
 IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
@@ -35,6 +47,10 @@
   OS << "error: " << libipt_error_message;
 }
 
+DecodedInstruction::operator bool() const {
+  return !IsLibiptError(libipt_error);
+}
+
 size_t DecodedThread::GetInstructionsCount() const {
   return m_instruction_ips.size();
 }
@@ -93,15 +109,26 @@
   }
 }
 
-void DecodedThread::AppendInstruction(const pt_insn &insn) {
-  m_instruction_ips.emplace_back(insn.ip);
-  m_instruction_sizes.emplace_back(insn.size);
-  m_instruction_classes.emplace_back(insn.iclass);
-}
+void DecodedThread::Append(const DecodedInstruction &insn) {
+  if (!insn) {
+    // End of stream shouldn't be a public error
+    if (IsEndOfStream(insn.libipt_error))
+      return;
+
+    AppendError(make_error<IntelPTError>(insn.libipt_error, insn.pt_insn.ip));
+  } else {
+    m_instruction_ips.emplace_back(insn.pt_insn.ip);
+    m_instruction_sizes.emplace_back(insn.pt_insn.size);
+    m_instruction_classes.emplace_back(insn.pt_insn.iclass);
+  }
 
-void DecodedThread::AppendInstruction(const pt_insn &insn, uint64_t tsc) {
-  AppendInstruction(insn);
-  RecordTscForLastInstruction(tsc);
+  if (insn.tsc)
+    RecordTscForLastInstruction(*insn.tsc);
+
+  if (insn.events) {
+    m_events.try_emplace(m_instruction_ips.size() - 1, insn.events);
+    m_events_stats.RecordEventsForInstruction(insn.events);
+  }
 }
 
 void DecodedThread::AppendError(llvm::Error &&error) {
@@ -111,22 +138,45 @@
   m_instruction_classes.emplace_back(pt_insn_class::ptic_error);
 }
 
-void DecodedThread::AppendError(llvm::Error &&error, uint64_t tsc) {
+void DecodedThread::SetAsFailed(llvm::Error &&error) {
   AppendError(std::move(error));
-  RecordTscForLastInstruction(tsc);
 }
 
-void DecodedThread::LibiptErrors::RecordError(int libipt_error_code) {
-  libipt_errors[pt_errstr(pt_errcode(libipt_error_code))]++;
+lldb::TraceEvents DecodedThread::GetEvents(int insn_index) {
+  auto it = m_events.find(insn_index);
+  if (it != m_events.end())
+    return it->second;
+  return (TraceEvents)0;
+}
+
+void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) {
+  libipt_errors_counts[pt_errstr(pt_errcode(libipt_error_code))]++;
   total_count++;
 }
 
 void DecodedThread::RecordTscError(int libipt_error_code) {
-  m_tsc_errors.RecordError(libipt_error_code);
+  m_tsc_errors_stats.RecordError(libipt_error_code);
 }
 
-const DecodedThread::LibiptErrors &DecodedThread::GetTscErrors() const {
-  return m_tsc_errors;
+const DecodedThread::LibiptErrorsStats &
+DecodedThread::GetTscErrorsStats() const {
+  return m_tsc_errors_stats;
+}
+
+const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const {
+  return m_events_stats;
+}
+
+void DecodedThread::EventsStats::RecordEventsForInstruction(
+    lldb::TraceEvents events) {
+  if (!events)
+    return;
+
+  total_instructions_with_events++;
+  trace_event_utils::ForEachEvent(events, [&](TraceEvents event) {
+    events_counts[event]++;
+    total_count++;
+  });
 }
 
 Optional<DecodedThread::TscRange> DecodedThread::CalculateTscRange(
@@ -187,7 +237,7 @@
          sizeof(pt_insn::size) * m_instruction_sizes.size() +
          sizeof(pt_insn::iclass) * m_instruction_classes.size() +
          (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() +
-         m_errors.getMemorySize();
+         m_errors.getMemorySize() + m_events.getMemorySize();
 }
 
 DecodedThread::TscRange::TscRange(std::map<size_t, uint64_t>::const_iterator it,
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1121,12 +1121,15 @@
   def thread_trace_dump_instructions_show_tsc : Option<"tsc", "t">, Group<1>,
     Desc<"For each instruction, print the corresponding timestamp counter if "
     "available.">;
+  def thread_trace_dump_instructions_hide_events : Option<"events", "e">,
+    Group<1>,
+    Desc<"Dump the events that happened during the execution of the target.">;
   def thread_trace_dump_instructions_continue: Option<"continue", "C">,
     Group<1>,
-    Desc<"Continue dumping instructions right where the previous invocation of this "
-    "command was left, or from the beginning if this is the first invocation. The --skip "
-    "argument is discarded and the other arguments are preserved from the previous "
-    "invocation when possible.">;
+    Desc<"Continue dumping instructions right where the previous invocation of "
+    "this command was left, or from the beginning if this is the first "
+    "invocation. The --skip argument is discarded and the other arguments are "
+    "preserved from the previous invocation when possible.">;
 }
 
 let Command = "thread trace dump info" in {
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -2156,6 +2156,10 @@
         m_dumper_options.show_tsc = true;
         break;
       }
+      case 'e': {
+        m_dumper_options.show_events = true;
+        break;
+      }
       case 'C': {
         m_continue = true;
         break;
Index: lldb/include/lldb/lldb-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-enumerations.h
+++ lldb/include/lldb/lldb-enumerations.h
@@ -1147,6 +1147,15 @@
   eTraceCounterTSC,
 };
 
+// Events that might happen during a trace session.
+FLAGS_ENUM(TraceEvents){
+    // Tracing was paused. If instructions were executed after pausing
+    // and before resuming, the TraceCursor used to traverse the trace
+    // should provide an error signalinig this data loss.
+    eTraceEventPaused = (1u << 0),
+};
+LLDB_MARK_AS_BITMASK_ENUM(TraceEvents)
+
 } // namespace lldb
 
 #endif // LLDB_LLDB_ENUMERATIONS_H
Index: lldb/include/lldb/Target/TraceInstructionDumper.h
===================================================================
--- lldb/include/lldb/Target/TraceInstructionDumper.h
+++ lldb/include/lldb/Target/TraceInstructionDumper.h
@@ -26,6 +26,8 @@
   /// For each instruction, print the corresponding timestamp counter if
   /// available.
   bool show_tsc = false;
+  /// Dump the events that happened between instructions.
+  bool show_events = false;
   /// Optional custom id to start traversing from.
   llvm::Optional<uint64_t> id = llvm::None;
   /// Optional number of instructions to skip from the starting position
@@ -79,6 +81,8 @@
   ///     \b true if the cursor moved.
   bool TryMoveOneStep();
 
+  void PrintEvents();
+
   lldb::TraceCursorUP m_cursor_up;
   TraceInstructionDumperOptions m_options;
   Stream &m_s;
Index: lldb/include/lldb/Target/TraceCursor.h
===================================================================
--- lldb/include/lldb/Target/TraceCursor.h
+++ lldb/include/lldb/Target/TraceCursor.h
@@ -234,9 +234,17 @@
   /// \param[in] counter_type
   ///    The counter type.
   /// \return
-  ///     The value of the counter or \b llvm::None if not available.
+  ///    The value of the counter or \b llvm::None if not available.
   virtual llvm::Optional<uint64_t> GetCounter(lldb::TraceCounter counter_type) = 0;
 
+  /// Get a bitmask with a list of events that happened chronologically right
+  /// before the current instruction or error, but after the previous
+  /// instruction.
+  ///
+  /// \return
+  ///   The bitmask of events.
+  virtual lldb::TraceEvents GetEvents() = 0;
+
   /// \return
   ///     The \a lldb::TraceInstructionControlFlowType categories the
   ///     instruction the cursor is pointing at falls into. If the cursor points
@@ -254,6 +262,29 @@
   bool m_forwards = false;
 };
 
+namespace trace_event_utils {
+/// Convert an individual event to a display string.
+///
+/// \param[in] event
+///     An individual event.
+///
+/// \return
+///     A display string for that event, or nullptr if wrong data is passed
+///     in.
+const char *EventToDisplayString(lldb::TraceEvents event);
+
+/// Invoke the given callback for each individual event of the given events
+/// bitmask.
+///
+/// \param[in] events
+///     A list of events to inspect.
+///
+/// \param[in] callback
+///     The callback to invoke for each event.
+void ForEachEvent(lldb::TraceEvents events,
+                  std::function<void(lldb::TraceEvents event)> callback);
+} // namespace trace_event_utils
+
 } // namespace lldb_private
 
 #endif // LLDB_TARGET_TRACE_CURSOR_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to