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) _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits