wallace created this revision.
wallace added reviewers: jj10306, zrthxn.
Herald added a project: All.
wallace requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

A trace might contain events traced during the target's execution. For
example, a thread might be paused for some period of time due to context
switches or breakpoints, which actually force a context switch. Not only
that, a trace might be paused because the CPU decides to trace only a
specific part of the target, like the address filtering provided by
intel pt, which will cause pause events. Besides this case, other kinds
of events might exist.

This patch adds the method `TraceCursor::GetEvents()`` that returns the
list of events that happened right before the instruction being pointed
at by the cursor. Some refactors were done to make this change simpler.

Besides this new API, the instruction dumper now supports the -e flag
which shows pause events, like in the following example, where pauses
happened due to breakpoints.

  thread #1: tid = 2717361
    a.out`main + 20 at main.cpp:27:20
      0: 0x00000000004023d9    leaq   -0x1200(%rbp), %rax
    [paused]
      1: 0x00000000004023e0    movq   %rax, %rdi
    [paused]
      2: 0x00000000004023e3    callq  0x403a62                  ; 
std::vector<int, std::allocator<int> >::vector at stl_vector.h:391:7
    a.out`std::vector<int, std::allocator<int> >::vector() at stl_vector.h:391:7
      3: 0x0000000000403a62    pushq  %rbp
      4: 0x0000000000403a63    movq   %rsp, %rbp

The `dump info` command has also been updated and now it shows the
number of instructions that have associated events.


Repository:
  rG LLVM Github Monorepo

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/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,15 @@
 
   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:
+    Total number of instructions with events: 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,15 @@
 
   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:
+    Total number of instructions with events: 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;
+
+  TraceEvents events = m_cursor_up->GetEvents();
+  if (events & lldb::eTraceEventPaused) {
+    m_s << "  [paused]\n";
+  }
+}
+
 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/Plugins/Trace/intel-pt/TraceIntelPT.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -142,6 +142,9 @@
         s.Format("    {0}: {1:2}s\n", name, duration.count() / 1000.0);
       });
 
+  s << "\n  Events:\n";
+  s.Format("    Total number of instructions with events: {0}\n", decoded_trace_sp->GetInstructionsWithEventsCount());
+
   s << "\n  Errors:\n";
   const DecodedThread::LibiptErrors &tsc_errors =
       decoded_trace_sp->GetTscErrors();
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,30 @@
   }
 
 private:
+  /// Invoke the low level function \a pt_insn_next and store the decoded
+  /// instruction in the given \a DecodedInstruction.
+  ///
+  /// \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 +96,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 +135,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 +145,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 = (TraceEvents)(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 +230,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 +301,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 iff 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
@@ -114,17 +144,16 @@
   /// 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
@@ -191,6 +220,10 @@
   ///   An error returned by the libipt library.
   void RecordTscError(int libipt_error_code);
 
+  /// \return
+  ///   The number of instructions with associated events.
+  size_t GetInstructionsWithEventsCount() const;
+
   /// The approximate size in bytes used by this instance,
   /// including all the already decoded instructions.
   size_t CalculateApproximateMemoryUsage() const;
@@ -198,6 +231,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);
@@ -225,9 +261,9 @@
   // 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;
+  /// 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;
   /// All occurrences of libipt errors when decoding TSCs.
   LibiptErrors m_tsc_errors;
   /// Total amount of time spent decoding.
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,10 +19,22 @@
 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)
-    : m_libipt_error_code(libipt_error_code), m_address(address) {
+    : m_libipt_error_code(-std::abs(libipt_error_code)), m_address(address) {
   assert(libipt_error_code < 0);
 }
 
@@ -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,22 @@
   }
 }
 
-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::AppendInstruction(const pt_insn &insn, uint64_t tsc) {
-  AppendInstruction(insn);
-  RecordTscForLastInstruction(tsc);
+void DecodedThread::Append(const DecodedInstruction &insn) {
+  if (insn.libipt_error < 0) {
+    // End of stream shouldn't be a public error
+    if (insn.libipt_error == -pte_eos)
+      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);
+  }
+  if (insn.tsc)
+    RecordTscForLastInstruction(*insn.tsc);
+  if (insn.events)
+    m_events.try_emplace(m_instruction_ips.size() - 1, insn.events);
 }
 
 void DecodedThread::AppendError(llvm::Error &&error) {
@@ -111,9 +134,15 @@
   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);
+}
+
+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::LibiptErrors::RecordError(int libipt_error_code) {
@@ -182,12 +211,16 @@
   return std::make_unique<TraceCursorIntelPT>(m_thread_sp, shared_from_this());
 }
 
+size_t DecodedThread::GetInstructionsWithEventsCount() const {
+  return m_events.size();
+}
+
 size_t DecodedThread::CalculateApproximateMemoryUsage() const {
   return sizeof(pt_insn::ip) * m_instruction_ips.size() +
          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,14 @@
   eTraceCounterTSC,
 };
 
+// Events that might happen during a trace session.
+FLAGS_ENUM(TraceEvents){
+    // Tracing was paused. If instructions were executed after resuming,
+    // the TraceCursor used to traverse the trace should provide an error
+    // signalinig this data loss.
+    eTraceEventPaused = (1u << 0),
+};
+
 } // 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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to