[Lldb-commits] [lldb] 50f9367 - Add LoadTraceFromFile to SBDebugger and SBTrace

Jakob Johnson via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 20 12:06:10 PDT 2022


Author: Jakob Johnson
Date: 2022-06-20T11:54:47-07:00
New Revision: 50f9367960725b450a9ef779d73e32a35031ee70

URL: https://github.com/llvm/llvm-project/commit/50f9367960725b450a9ef779d73e32a35031ee70
DIFF: https://github.com/llvm/llvm-project/commit/50f9367960725b450a9ef779d73e32a35031ee70.diff

LOG: Add LoadTraceFromFile to SBDebugger and SBTrace

Add trace load functionality to SBDebugger via the `LoadTraceFromFile` method.
Update intelpt test case class to have `testTraceLoad` method so we can take advantage of
the testApiAndSB decorator to test both the CLI and SB without duplicating code.

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

Added: 
    

Modified: 
    lldb/bindings/interface/SBDebugger.i
    lldb/include/lldb/API/SBDebugger.h
    lldb/include/lldb/API/SBFileSpec.h
    lldb/include/lldb/API/SBTrace.h
    lldb/include/lldb/Target/Trace.h
    lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
    lldb/source/API/SBDebugger.cpp
    lldb/source/API/SBTrace.cpp
    lldb/source/Commands/CommandObjectTrace.cpp
    lldb/source/Target/Trace.cpp
    lldb/test/API/commands/trace/TestTraceLoad.py

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBDebugger.i b/lldb/bindings/interface/SBDebugger.i
index 2239f049c6514..5d51a6ac20d2f 100644
--- a/lldb/bindings/interface/SBDebugger.i
+++ b/lldb/bindings/interface/SBDebugger.i
@@ -542,6 +542,8 @@ Example: ::
     lldb::SBError
     RunREPL (lldb::LanguageType language, const char *repl_options);
 
+    SBTrace LoadTraceFromFile(SBError &error, const SBFileSpec &trace_description_file);
+
 #ifdef SWIGPYTHON
     %pythoncode%{
     def __iter__(self):

diff  --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index 6a23077175b92..b9a9b593d0ad4 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -391,6 +391,17 @@ class LLDB_API SBDebugger {
 
   SBError RunREPL(lldb::LanguageType language, const char *repl_options);
 
+  /// Load a trace from a trace description file and create Targets,
+  /// Processes and Threads based on the contents of such file.
+  ///
+  /// \param[out] error
+  ///   An error if the trace could not be created.
+  ///
+  /// \param[in] trace_description_file
+  ///   The file containing the necessary information to load the trace.
+  SBTrace LoadTraceFromFile(SBError &error,
+                            const SBFileSpec &trace_description_file);
+
 private:
   friend class SBCommandInterpreter;
   friend class SBInputReader;
@@ -398,6 +409,7 @@ class LLDB_API SBDebugger {
   friend class SBProcess;
   friend class SBSourceManager;
   friend class SBTarget;
+  friend class SBTrace;
 
   lldb::SBTarget FindTargetWithLLDBProcess(const lldb::ProcessSP &processSP);
 

diff  --git a/lldb/include/lldb/API/SBFileSpec.h b/lldb/include/lldb/API/SBFileSpec.h
index a2f02ac78208b..a286912f109e5 100644
--- a/lldb/include/lldb/API/SBFileSpec.h
+++ b/lldb/include/lldb/API/SBFileSpec.h
@@ -74,6 +74,7 @@ class LLDB_API SBFileSpec {
   friend class SBSourceManager;
   friend class SBTarget;
   friend class SBThread;
+  friend class SBTrace;
 
   SBFileSpec(const lldb_private::FileSpec &fspec);
 

diff  --git a/lldb/include/lldb/API/SBTrace.h b/lldb/include/lldb/API/SBTrace.h
index 1685caaf4efa3..d5cf30f56637f 100644
--- a/lldb/include/lldb/API/SBTrace.h
+++ b/lldb/include/lldb/API/SBTrace.h
@@ -12,8 +12,6 @@
 #include "lldb/API/SBDefines.h"
 #include "lldb/API/SBError.h"
 
-class TraceImpl;
-
 namespace lldb {
 
 class LLDB_API SBTrace {
@@ -23,6 +21,10 @@ class LLDB_API SBTrace {
 
   SBTrace(const lldb::TraceSP &trace_sp);
 
+  /// See SBDebugger::LoadTraceFromFile.
+  static SBTrace LoadTraceFromFile(SBError &error, SBDebugger &debugger,
+                                   const SBFileSpec &trace_description_file);
+
   /// \return
   ///     A description of the parameters to use for the \a SBTrace::Start
   ///     method, or \b null if the object is invalid.

diff  --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h
index 2fee3c9274959..216394bdd1847 100644
--- a/lldb/include/lldb/Target/Trace.h
+++ b/lldb/include/lldb/Target/Trace.h
@@ -137,6 +137,23 @@ class Trace : public PluginInterface,
   static llvm::Expected<llvm::StringRef>
   FindPluginSchema(llvm::StringRef plugin_name);
 
+  /// Load a trace from a trace description file and create Targets,
+  /// Processes and Threads based on the contents of such file.
+  ///
+  /// \param[in] debugger
+  ///     The debugger instance where new Targets will be created as part of the
+  ///     JSON data parsing.
+  ///
+  /// \param[in] trace_description_file
+  ///   The file containing the necessary information to load the trace.
+  ///
+  /// \return
+  ///     A \a TraceSP instance, or an \a llvm::Error if loading the trace
+  ///     fails.
+  static llvm::Expected<lldb::TraceSP>
+  LoadPostMortemTraceFromFile(Debugger &debugger,
+                              const FileSpec &trace_description_file);
+
   /// Get the command handle for the "process trace start" command.
   virtual lldb::CommandObjectSP
   GetProcessTraceStartCommand(CommandInterpreter &interpreter) = 0;

diff  --git a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
index 501429036c718..436cf6e868570 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/intelpt/intelpt_testcase.py
@@ -133,3 +133,13 @@ def traceStopThread(self, thread=None, error=False, substrs=None):
             if thread is not None:
                 command += " " + str(thread.GetIndexID())
             self.expect(command, error=error, substrs=substrs)
+
+    def traceLoad(self, traceDescriptionFilePath="trace.json", error=False, substrs=None):
+        if self.USE_SB_API:
+            traceDescriptionFile = lldb.SBFileSpec(traceDescriptionFilePath, True)
+            loadTraceError = lldb.SBError()
+            _trace = self.dbg.LoadTraceFromFile(loadTraceError, traceDescriptionFile)
+            self.assertSBError(loadTraceError, error)
+        else:
+            command = f"trace load -v {traceDescriptionFilePath}"
+            self.expect(command, error=error, substrs=substrs)

diff  --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index ea340e3a44792..37469989dc5b5 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -27,6 +27,7 @@
 #include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBTarget.h"
 #include "lldb/API/SBThread.h"
+#include "lldb/API/SBTrace.h"
 #include "lldb/API/SBTypeCategory.h"
 #include "lldb/API/SBTypeFilter.h"
 #include "lldb/API/SBTypeFormat.h"
@@ -1633,3 +1634,10 @@ void SBDebugger::SetLoggingCallback(lldb::LogOutputCallback log_callback,
     return m_opaque_sp->SetLoggingCallback(log_callback, baton);
   }
 }
+
+SBTrace
+SBDebugger::LoadTraceFromFile(SBError &error,
+                              const SBFileSpec &trace_description_file) {
+  LLDB_INSTRUMENT_VA(this, error, trace_description_file);
+  return SBTrace::LoadTraceFromFile(error, *this, trace_description_file);
+}

diff  --git a/lldb/source/API/SBTrace.cpp b/lldb/source/API/SBTrace.cpp
index 64a675e2e16cd..fe90032370733 100644
--- a/lldb/source/API/SBTrace.cpp
+++ b/lldb/source/API/SBTrace.cpp
@@ -9,6 +9,7 @@
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/Instrumentation.h"
 
+#include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBStructuredData.h"
 #include "lldb/API/SBThread.h"
 #include "lldb/API/SBTrace.h"
@@ -19,6 +20,7 @@
 
 using namespace lldb;
 using namespace lldb_private;
+using namespace llvm;
 
 SBTrace::SBTrace() { LLDB_INSTRUMENT_VA(this); }
 
@@ -26,6 +28,21 @@ SBTrace::SBTrace(const lldb::TraceSP &trace_sp) : m_opaque_sp(trace_sp) {
   LLDB_INSTRUMENT_VA(this, trace_sp);
 }
 
+SBTrace SBTrace::LoadTraceFromFile(SBError &error, SBDebugger &debugger,
+                                   const SBFileSpec &trace_description_file) {
+  LLDB_INSTRUMENT_VA(error, debugger, trace_description_file);
+
+  Expected<lldb::TraceSP> trace_or_err = Trace::LoadPostMortemTraceFromFile(
+      debugger.ref(), trace_description_file.ref());
+
+  if (!trace_or_err) {
+    error.SetErrorString(toString(trace_or_err.takeError()).c_str());
+    return SBTrace();
+  }
+
+  return SBTrace(trace_or_err.get());
+}
+
 const char *SBTrace::GetStartConfigurationHelp() {
   LLDB_INSTRUMENT_VA(this);
   return m_opaque_sp ? m_opaque_sp->GetStartConfigurationHelp() : nullptr;

diff  --git a/lldb/source/Commands/CommandObjectTrace.cpp b/lldb/source/Commands/CommandObjectTrace.cpp
index 53f1b0a32e607..d69b223c5a707 100644
--- a/lldb/source/Commands/CommandObjectTrace.cpp
+++ b/lldb/source/Commands/CommandObjectTrace.cpp
@@ -85,41 +85,26 @@ class CommandObjectTraceLoad : public CommandObjectParsed {
     if (command.size() != 1) {
       result.AppendError(
           "a single path to a JSON file containing a trace session"
-          "is required");
+          " is required");
       return false;
     }
 
-    auto end_with_failure = [&result](llvm::Error err) -> bool {
-      result.AppendErrorWithFormat("%s\n",
-                                   llvm::toString(std::move(err)).c_str());
-      return false;
-    };
+    const FileSpec trace_description_file(command[0].ref());
 
-    FileSpec json_file(command[0].ref());
+    llvm::Expected<lldb::TraceSP> trace_or_err =
+        Trace::LoadPostMortemTraceFromFile(GetDebugger(),
+                                           trace_description_file);
 
-    auto buffer_or_error = llvm::MemoryBuffer::getFile(json_file.GetPath());
-    if (!buffer_or_error) {
-      return end_with_failure(llvm::createStringError(
-          std::errc::invalid_argument, "could not open input file: %s - %s.",
-          json_file.GetPath().c_str(),
-          buffer_or_error.getError().message().c_str()));
+    if (!trace_or_err) {
+      result.AppendErrorWithFormat(
+          "%s\n", llvm::toString(trace_or_err.takeError()).c_str());
+      return false;
     }
 
-    llvm::Expected<json::Value> session_file =
-        json::parse(buffer_or_error.get()->getBuffer().str());
-    if (!session_file)
-      return end_with_failure(session_file.takeError());
-
-    if (Expected<lldb::TraceSP> traceOrErr =
-            Trace::FindPluginForPostMortemProcess(
-                GetDebugger(), *session_file,
-                json_file.GetDirectory().AsCString())) {
-      lldb::TraceSP trace_sp = traceOrErr.get();
-      if (m_options.m_verbose && trace_sp)
-        result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
-                                        trace_sp->GetPluginName());
-    } else
-      return end_with_failure(traceOrErr.takeError());
+    if (m_options.m_verbose) {
+      result.AppendMessageWithFormatv("loading trace with plugin {0}\n",
+                                      trace_or_err.get()->GetPluginName());
+    }
 
     result.SetStatus(eReturnStatusSuccessFinishResult);
     return true;

diff  --git a/lldb/source/Target/Trace.cpp b/lldb/source/Target/Trace.cpp
index ac7fd7717621b..6a022a27d0340 100644
--- a/lldb/source/Target/Trace.cpp
+++ b/lldb/source/Target/Trace.cpp
@@ -89,6 +89,30 @@ static Error createInvalidPlugInError(StringRef plugin_name) {
       plugin_name.data());
 }
 
+Expected<lldb::TraceSP>
+Trace::LoadPostMortemTraceFromFile(Debugger &debugger,
+                                   const FileSpec &trace_description_file) {
+
+  auto buffer_or_error =
+      MemoryBuffer::getFile(trace_description_file.GetPath());
+  if (!buffer_or_error) {
+    return createStringError(std::errc::invalid_argument,
+                             "could not open input file: %s - %s.",
+                             trace_description_file.GetPath().c_str(),
+                             buffer_or_error.getError().message().c_str());
+  }
+
+  Expected<json::Value> session_file =
+      json::parse(buffer_or_error.get()->getBuffer().str());
+  if (!session_file) {
+    return session_file.takeError();
+  }
+
+  return Trace::FindPluginForPostMortemProcess(
+      debugger, *session_file,
+      trace_description_file.GetDirectory().AsCString());
+}
+
 Expected<lldb::TraceSP>
 Trace::FindPluginForPostMortemProcess(Debugger &debugger,
                                       const json::Value &trace_session_file,

diff  --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py
index e5746989ba1c0..80783dbf0ac30 100644
--- a/lldb/test/API/commands/trace/TestTraceLoad.py
+++ b/lldb/test/API/commands/trace/TestTraceLoad.py
@@ -7,10 +7,11 @@
 class TestTraceLoad(TraceIntelPTTestCaseBase):
     NO_DEBUG_INFO_TESTCASE = True
 
+    @testSBAPIAndCommands
     def testLoadMultiCoreTrace(self):
         src_dir = self.getSourceDir()
-        trace_definition_file = os.path.join(src_dir, "intelpt-multi-core-trace", "trace.json")
-        self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-multi-core-trace", "trace.json")
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"])
         self.expect("thread trace dump instructions 2 -t",
           substrs=["19521: [tsc=0x008fb5211c143fd8] error: expected tracing enabled event",
                    "m.out`foo() + 65 at multi_thread.cpp:12:21",
@@ -19,10 +20,11 @@ def testLoadMultiCoreTrace(self):
           substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
                    "m.out`bar() + 26 at multi_thread.cpp:20:6"])
 
+    @testSBAPIAndCommands
     def testLoadMultiCoreTraceWithStringNumbers(self):
         src_dir = self.getSourceDir()
-        trace_definition_file = os.path.join(src_dir, "intelpt-multi-core-trace", "trace_with_string_numbers.json")
-        self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-multi-core-trace", "trace_with_string_numbers.json")
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"])
         self.expect("thread trace dump instructions 2 -t",
           substrs=["19521: [tsc=0x008fb5211c143fd8] error: expected tracing enabled event",
                    "m.out`foo() + 65 at multi_thread.cpp:12:21",
@@ -31,10 +33,11 @@ def testLoadMultiCoreTraceWithStringNumbers(self):
           substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
                    "m.out`bar() + 26 at multi_thread.cpp:20:6"])
 
+    @testSBAPIAndCommands
     def testLoadMultiCoreTraceWithMissingThreads(self):
         src_dir = self.getSourceDir()
-        trace_definition_file = os.path.join(src_dir, "intelpt-multi-core-trace", "trace_missing_threads.json")
-        self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-multi-core-trace", "trace_missing_threads.json")
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"])
         self.expect("thread trace dump instructions 3 -t",
           substrs=["19521: [tsc=0x008fb5211c143fd8] error: expected tracing enabled event",
                    "m.out`foo() + 65 at multi_thread.cpp:12:21",
@@ -43,10 +46,11 @@ def testLoadMultiCoreTraceWithMissingThreads(self):
           substrs=["67910: [tsc=0x008fb5211bfdf270] 0x0000000000400bd7    addl   $0x1, -0x4(%rbp)",
                    "m.out`bar() + 26 at multi_thread.cpp:20:6"])
 
+    @testSBAPIAndCommands
     def testLoadTrace(self):
         src_dir = self.getSourceDir()
-        trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace.json")
-        self.expect("trace load -v " + trace_definition_file, substrs=["intel-pt"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace.json")
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, substrs=["intel-pt"])
 
         target = self.dbg.GetSelectedTarget()
         process = target.GetProcess()
@@ -88,11 +92,13 @@ def testLoadTrace(self):
   Errors:
     Number of TSC decoding errors: 0'''])
 
+    @testSBAPIAndCommands
     def testLoadInvalidTraces(self):
         src_dir = self.getSourceDir()
+
         # We test first an invalid type
-        self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad.json"), error=True,
-          substrs=['''error: expected object at traceSession.processes[0]
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace_bad.json")
+        expected_substrs = ['''error: expected object at traceSession.processes[0]
 
 Context:
 {
@@ -114,15 +120,19 @@ def testLoadInvalidTraces(self):
     "family": integer,
     "model": integer,
     "stepping": integer
-  },'''])
+  },''']
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
+
 
         # Now we test a wrong cpu family field in the global session file
-        self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad2.json"), error=True,
-            substrs=['error: expected uint64_t at traceSession.cpuInfo.family', "Context", "Schema"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace_bad2.json")
+        expected_substrs = ['error: expected uint64_t at traceSession.cpuInfo.family', "Context", "Schema"]
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
+
 
         # Now we test a missing field in the intel-pt settings
-        self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad4.json"), error=True,
-            substrs=['''error: missing value at traceSession.cpuInfo.family
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace_bad4.json")
+        expected_substrs = ['''error: missing value at traceSession.cpuInfo.family
 
 Context:
 {
@@ -133,15 +143,20 @@ def testLoadInvalidTraces(self):
   },
   "processes": [],
   "type": "intel-pt"
-}''', "Schema"])
+}''', "Schema"]
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
+
 
         # Now we test an incorrect load address in the intel-pt settings
-        self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad5.json"), error=True,
-            substrs=['error: missing value at traceSession.processes[1].pid', "Schema"])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace_bad5.json")
+        expected_substrs = ['error: missing value at traceSession.processes[1].pid', "Schema"]
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
+
 
         # The following wrong schema will have a valid target and an invalid one. In the case of failure,
         # no targets should be created.
         self.assertEqual(self.dbg.GetNumTargets(), 0)
-        self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad3.json"), error=True,
-            substrs=['error: missing value at traceSession.processes[1].pid'])
+        trace_description_file_path = os.path.join(src_dir, "intelpt-trace", "trace_bad3.json")
+        expected_substrs = ['error: missing value at traceSession.processes[1].pid']
+        self.traceLoad(traceDescriptionFilePath=trace_description_file_path, error=True, substrs=expected_substrs)
         self.assertEqual(self.dbg.GetNumTargets(), 0)


        


More information about the lldb-commits mailing list