[Lldb-commits] [lldb] 345ace0 - [trace] [intel pt] Create a "thread trace dump stats" command

Walter Erquinigo via lldb-commits lldb-commits at lists.llvm.org
Wed Jul 21 09:59:55 PDT 2021


Author: Walter Erquinigo
Date: 2021-07-21T09:50:15-07:00
New Revision: 345ace026b6e5cdbc38d207291e4b399d72e62ee

URL: https://github.com/llvm/llvm-project/commit/345ace026b6e5cdbc38d207291e4b399d72e62ee
DIFF: https://github.com/llvm/llvm-project/commit/345ace026b6e5cdbc38d207291e4b399d72e62ee.diff

LOG: [trace] [intel pt] Create a "thread trace dump stats" command

When the user types that command 'thread trace dump info' and there's a running Trace session in LLDB, a raw trace in bytes should be printed; the command 'thread trace dump info all' should print the info for all the threads.

Original Author: hanbingwang

Reviewed By: clayborg, wallace

Differential Revision: https://reviews.llvm.org/D105717

Added: 
    lldb/test/API/commands/trace/TestTraceDumpInfo.py

Modified: 
    lldb/include/lldb/Target/Trace.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/IntelPTDecoder.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
    lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
    lldb/test/API/commands/trace/TestTraceLoad.py

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index c82d17b029557..f5654988b2015 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -140,6 +140,20 @@ class Trace : public PluginInterface,
   ///     trace.
   virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0;
 
+  /// Dump general info about a given thread's trace. Each Trace plug-in
+  /// decides which data to show.
+  ///
+  /// \param[in] thread
+  ///     The thread that owns the trace in question.
+  ///
+  /// \param[in] s
+  ///     The stream object where the info will be printed printed.
+  ///
+  /// \param[in] verbose
+  ///     If \b true, print detailed info
+  ///     If \b false, print compact info
+  virtual void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) = 0;
+
   /// Check if a thread is currently traced by this object.
   ///
   /// \param[in] thread

diff  --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 2f8772953af40..3b58d71048739 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -2138,6 +2138,83 @@ class CommandObjectTraceDumpInstructions
   std::map<lldb::tid_t, std::unique_ptr<TraceInstructionDumper>> m_dumpers;
 };
 
+// CommandObjectTraceDumpInfo
+#define LLDB_OPTIONS_thread_trace_dump_info
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpInfo : public CommandObjectIterateOverThreads {
+public:
+  class CommandOptions : public Options {
+  public:
+    CommandOptions() : Options() { OptionParsingStarting(nullptr); }
+
+    ~CommandOptions() override = default;
+
+    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+                          ExecutionContext *execution_context) override {
+      Status error;
+      const int short_option = m_getopt_table[option_idx].val;
+
+      switch (short_option) {
+      case 'v': {
+        m_verbose = true;
+        break;
+      }
+      default:
+        llvm_unreachable("Unimplemented option");
+      }
+      return error;
+    }
+
+    void OptionParsingStarting(ExecutionContext *execution_context) override {
+      m_verbose = false;
+    }
+
+    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+      return llvm::makeArrayRef(g_thread_trace_dump_info_options);
+    }
+
+    // Instance variables to hold the values for command options.
+    bool m_verbose;
+  };
+
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    Target &target = m_exe_ctx.GetTargetRef();
+    result.GetOutputStream().Printf(
+        "Trace technology: %s\n",
+        target.GetTrace()->GetPluginName().AsCString());
+    return CommandObjectIterateOverThreads::DoExecute(command, result);
+  }
+
+  CommandObjectTraceDumpInfo(CommandInterpreter &interpreter)
+      : CommandObjectIterateOverThreads(
+            interpreter, "thread trace dump info",
+            "Dump the traced information for one or more threads.  If no "
+            "threads are specified, show the current thread.  Use the "
+            "thread-index \"all\" to see all threads.",
+            nullptr,
+            eCommandRequiresProcess | eCommandTryTargetAPILock |
+                eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+                eCommandProcessMustBeTraced),
+        m_options() {}
+
+  ~CommandObjectTraceDumpInfo() override = default;
+
+  Options *GetOptions() override { return &m_options; }
+
+protected:
+  bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
+    const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
+    ThreadSP thread_sp =
+        m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
+    trace_sp->DumpTraceInfo(*thread_sp, result.GetOutputStream(),
+                            m_options.m_verbose);
+    return true;
+  }
+
+  CommandOptions m_options;
+};
+
 // CommandObjectMultiwordTraceDump
 class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
 public:
@@ -2150,6 +2227,8 @@ class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
     LoadSubCommand(
         "instructions",
         CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
+    LoadSubCommand(
+        "info", CommandObjectSP(new CommandObjectTraceDumpInfo(interpreter)));
   }
   ~CommandObjectMultiwordTraceDump() override = default;
 };

diff  --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index d0bc80c74d55d..9c9b7c6e9b829 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1070,6 +1070,11 @@ let Command = "thread trace dump instructions" in {
     Desc<"For each instruction, print the corresponding timestamp counter if available.">;
 }
 
+let Command = "thread trace dump info" in {
+  def thread_trace_dump_info_verbose : Option<"verbose", "v">, Group<1>,
+    Desc<"show verbose thread trace dump info">;
+}
+
 let Command = "type summary add" in {
   def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
     Desc<"Add this to the given category instead of the default one.">;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
index 970a7970c0fb3..4822a786c68c1 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -61,6 +61,7 @@ Error IntelPTInstruction::ToError() const {
   return make_error<StringError>(m_error->message(),
                                  m_error->convertToErrorCode());
 }
+size_t DecodedThread::GetRawTraceSize() const { return m_raw_trace_size; }
 
 TraceInstructionControlFlowType
 IntelPTInstruction::GetControlFlowType(lldb::addr_t next_load_address) const {
@@ -103,8 +104,10 @@ DecodedThread::DecodedThread(ThreadSP thread_sp, Error error)
 }
 
 DecodedThread::DecodedThread(ThreadSP thread_sp,
-                             std::vector<IntelPTInstruction> &&instructions)
-    : m_thread_sp(thread_sp), m_instructions(std::move(instructions)) {
+                             std::vector<IntelPTInstruction> &&instructions,
+                             size_t raw_trace_size)
+    : m_thread_sp(thread_sp), m_instructions(std::move(instructions)),
+      m_raw_trace_size(raw_trace_size) {
   if (m_instructions.empty())
     m_instructions.emplace_back(
         createStringError(inconvertibleErrorCode(), "empty trace"));

diff  --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
index 135d938dfc57b..592c402cd0e50 100644
--- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -127,7 +127,8 @@ class IntelPTInstruction {
 class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
 public:
   DecodedThread(lldb::ThreadSP thread_sp,
-                std::vector<IntelPTInstruction> &&instructions);
+                std::vector<IntelPTInstruction> &&instructions,
+                size_t raw_trace_size);
 
   /// Constructor with a single error signaling a complete failure of the
   /// decoding process.
@@ -143,9 +144,16 @@ class DecodedThread : public std::enable_shared_from_this<DecodedThread> {
   /// Get a new cursor for the decoded thread.
   lldb::TraceCursorUP GetCursor();
 
+  /// Get the size in bytes of the corresponding Intel PT raw trace
+  ///
+  /// \return
+  ///   The size of the trace.
+  size_t GetRawTraceSize() const;
+
 private:
   lldb::ThreadSP m_thread_sp;
   std::vector<IntelPTInstruction> m_instructions;
+  size_t m_raw_trace_size;
 };
 
 using DecodedThreadSP = std::shared_ptr<DecodedThread>;

diff  --git a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
index 8c331841b54e6..aeba0081e5076 100644
--- a/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -211,7 +211,7 @@ DecodeInMemoryTrace(Process &process, TraceIntelPT &trace_intel_pt,
 
 static Expected<std::vector<IntelPTInstruction>>
 DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt,
-                const FileSpec &trace_file) {
+                const FileSpec &trace_file, size_t &raw_trace_size) {
   ErrorOr<std::unique_ptr<MemoryBuffer>> trace_or_error =
       MemoryBuffer::getFile(trace_file.GetPath());
   if (std::error_code err = trace_or_error.getError())
@@ -223,15 +223,17 @@ DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt,
       // following cast is safe.
       reinterpret_cast<uint8_t *>(const_cast<char *>(trace.getBufferStart())),
       trace.getBufferSize());
+  raw_trace_size = trace_data.size();
   return DecodeInMemoryTrace(process, trace_intel_pt, trace_data);
 }
 
 static Expected<std::vector<IntelPTInstruction>>
-DecodeLiveThread(Thread &thread, TraceIntelPT &trace) {
+DecodeLiveThread(Thread &thread, TraceIntelPT &trace, size_t &raw_trace_size) {
   Expected<std::vector<uint8_t>> buffer =
       trace.GetLiveThreadBuffer(thread.GetID());
   if (!buffer)
     return buffer.takeError();
+  raw_trace_size = buffer->size();
   if (Expected<pt_cpu> cpu_info = trace.GetCPUInfo())
     return DecodeInMemoryTrace(*thread.GetProcess(), trace,
                                MutableArrayRef<uint8_t>(*buffer));
@@ -250,11 +252,13 @@ PostMortemThreadDecoder::PostMortemThreadDecoder(
     : m_trace_thread(trace_thread), m_trace(trace) {}
 
 DecodedThreadSP PostMortemThreadDecoder::DoDecode() {
+  size_t raw_trace_size = 0;
   if (Expected<std::vector<IntelPTInstruction>> instructions =
           DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace,
-                          m_trace_thread->GetTraceFile()))
+                          m_trace_thread->GetTraceFile(), raw_trace_size))
     return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
-                                           std::move(*instructions));
+                                           std::move(*instructions),
+                                           raw_trace_size);
   else
     return std::make_shared<DecodedThread>(m_trace_thread->shared_from_this(),
                                            instructions.takeError());
@@ -264,10 +268,11 @@ LiveThreadDecoder::LiveThreadDecoder(Thread &thread, TraceIntelPT &trace)
     : m_thread_sp(thread.shared_from_this()), m_trace(trace) {}
 
 DecodedThreadSP LiveThreadDecoder::DoDecode() {
+  size_t raw_trace_size = 0;
   if (Expected<std::vector<IntelPTInstruction>> instructions =
-          DecodeLiveThread(*m_thread_sp, m_trace))
-    return std::make_shared<DecodedThread>(m_thread_sp,
-                                           std::move(*instructions));
+          DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size))
+    return std::make_shared<DecodedThread>(
+        m_thread_sp, std::move(*instructions), raw_trace_size);
   else
     return std::make_shared<DecodedThread>(m_thread_sp,
                                            instructions.takeError());

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
index efbc0e2ed7f47..7588db610fafa 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
@@ -108,6 +108,24 @@ lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) {
   return Decode(thread)->GetCursor();
 }
 
+void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
+  Optional<size_t> raw_size = GetRawTraceSize(thread);
+  s.Printf("\nthread #%u: tid = %" PRIu64, thread.GetIndexID(), thread.GetID());
+  if (!raw_size) {
+    s.Printf(", not traced\n");
+    return;
+  }
+  s.Printf("\n  Raw trace size: %zu bytes\n", *raw_size);
+  return;
+}
+
+Optional<size_t> TraceIntelPT::GetRawTraceSize(Thread &thread) {
+  if (IsTraced(thread))
+    return Decode(thread)->GetRawTraceSize();
+  else
+    return None;
+}
+
 Expected<pt_cpu> TraceIntelPT::GetCPUInfoForLiveProcess() {
   Expected<std::vector<uint8_t>> cpu_info = GetLiveProcessBinaryData("cpuInfo");
   if (!cpu_info)

diff  --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
index cd6fedb2e1616..e3b247112ae16 100644
--- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -67,6 +67,10 @@ class TraceIntelPT : public Trace {
 
   lldb::TraceCursorUP GetCursor(Thread &thread) override;
 
+  void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) override;
+
+  llvm::Optional<size_t> GetRawTraceSize(Thread &thread);
+
   void DoRefreshLiveProcessState(
       llvm::Expected<TraceGetStateResponse> state) override;
 

diff  --git a/lldb/test/API/commands/trace/TestTraceDumpInfo.py b/lldb/test/API/commands/trace/TestTraceDumpInfo.py
new file mode 100644
index 0000000000000..b06840865232b
--- /dev/null
+++ b/lldb/test/API/commands/trace/TestTraceDumpInfo.py
@@ -0,0 +1,41 @@
+import lldb
+from intelpt_testcase import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceDumpInfo(TraceIntelPTTestCaseBase):
+    mydir = TestBase.compute_mydir(__file__)
+
+    def testErrorMessages(self):
+        # We first check the output when there are no targets
+        self.expect("thread trace dump info",
+            substrs=["error: invalid target, create a target using the 'target create' command"],
+            error=True)
+
+        # We now check the output when there's a non-running target
+        self.expect("target create " +
+            os.path.join(self.getSourceDir(), "intelpt-trace", "a.out"))
+
+        self.expect("thread trace dump info",
+            substrs=["error: invalid process"],
+            error=True)
+
+        # Now we check the output when there's a running target without a trace
+        self.expect("b main")
+        self.expect("run")
+
+        self.expect("thread trace dump info",
+            substrs=["error: Process is not being traced"],
+            error=True)
+
+    def testDumpRawTraceSize(self):
+        self.expect("trace load -v " +
+        os.path.join(self.getSourceDir(), "intelpt-trace", "trace.json"),
+        substrs=["intel-pt"])
+
+        self.expect("thread trace dump info",
+            substrs=['''Trace technology: intel-pt
+
+thread #1: tid = 3842849
+  Raw trace size: 4096 bytes'''])

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index 0ea414f2b0594..896f0eade663f 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -33,7 +33,10 @@ def testLoadTrace(self):
         # check that the Process and Thread objects were created correctly
         self.expect("thread info", substrs=["tid = 3842849"])
         self.expect("thread list", substrs=["Process 1234 stopped", "tid = 3842849"])
+        self.expect("thread trace dump info", substrs=['''Trace technology: intel-pt
 
+thread #1: tid = 3842849
+  Raw trace size: 4096 bytes'''])
 
     def testLoadInvalidTraces(self):
         src_dir = self.getSourceDir()


        


More information about the lldb-commits mailing list