wallace updated this revision to Diff 295145.
wallace added a comment.
Herald added a reviewer: JDevlieghere.
Rebased and did some code cleanup. The output of the dump command looks like
this
Settings directory:
/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace
Trace files:
pid: '1234', tid: '3842849', trace file:
/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace/3842849.trace
Modules:
uuid: 6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A, load address:
0x0000000000400000, file:
/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace/a.out
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D86670/new/
https://reviews.llvm.org/D86670
Files:
lldb/include/lldb/Target/Target.h
lldb/include/lldb/Target/Trace.h
lldb/source/Commands/CommandObjectTrace.cpp
lldb/source/Commands/Options.td
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp
lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
lldb/source/Target/Target.cpp
lldb/source/Target/Trace.cpp
lldb/source/Target/TraceSettingsParser.cpp
lldb/test/API/commands/trace/TestTraceDump.py
lldb/test/API/commands/trace/TestTraceLoad.py
lldb/test/API/commands/trace/intelpt-trace/trace2.json
Index: lldb/test/API/commands/trace/intelpt-trace/trace2.json
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/intelpt-trace/trace2.json
@@ -0,0 +1,53 @@
+{
+ "trace": {
+ "type": "intel-pt",
+ "pt_cpu": {
+ "vendor": "intel",
+ "family": 6,
+ "model": 79,
+ "stepping": 1
+ }
+ },
+ "processes": [
+ {
+ "pid": 1,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 11,
+ "traceFile": "3842849.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+ }
+ ]
+ },
+ {
+ "pid": 2,
+ "triple": "x86_64-*-linux",
+ "threads": [
+ {
+ "tid": 21,
+ "traceFile": "3842849.trace"
+ },
+ {
+ "tid": 22,
+ "traceFile": "3842849.trace"
+ }
+ ],
+ "modules": [
+ {
+ "file": "a.out",
+ "systemPath": "a.out",
+ "loadAddress": "0x0000000000400000",
+ "uuid": "6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A"
+ }
+ ]
+ }
+ ]
+}
Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -92,3 +92,5 @@
self.expect("trace load -v " + os.path.join(src_dir, "intelpt-trace", "trace_bad3.json"), error=True,
substrs=['error: missing value at settings.processes[1].pid'])
self.assertEqual(self.dbg.GetNumTargets(), 0)
+
+ self.expect("trace dump", substrs=["error: no trace data in this target"])
Index: lldb/test/API/commands/trace/TestTraceDump.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/TestTraceDump.py
@@ -0,0 +1,49 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceDump(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ if 'intel-pt' not in configuration.enabled_plugins:
+ self.skipTest("The intel-pt test plugin is not enabled")
+
+
+ def testDumpTrace(self):
+ self.expect("trace dump", substrs=["error: no trace data in this target"])
+
+ src_dir = self.getSourceDir()
+ trace_definition_file = os.path.join(src_dir, "intelpt-trace", "trace2.json")
+ self.expect("trace load " + trace_definition_file)
+
+ # An invalid thread id should show an error message
+ self.expect("trace dump -t 5678", substrs=['error: the thread id "5678" does not exist in this target'])
+
+ # Only the specified thread should be printed
+ self.expect("trace dump -t 22", substrs=["Trace files"],
+ patterns=["pid: '2', tid: '22', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace",
+ "Modules:\n uuid: 6AA9A4E2-6F28-2F33-377D-59FECE874C71-5B41261A, load address: 0x0000000000400000, file: .*intelpt-trace/a.out"])
+
+ # Verbose should output the entire JSON settings besides the thread specific information
+ self.expect("trace dump -t 22 -v",
+ substrs=["Settings", "3842849.trace", "intel-pt", "Settings directory"],
+ patterns=["pid: '2', tid: '22', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace"])
+
+ # In this case all threads should be printed
+ self.expect("trace dump", substrs=["Trace files"],
+ patterns=["pid: '2', tid: '21', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace",
+ "pid: '2', tid: '22', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace"])
+
+ self.expect("trace dump -t 21 --thread-id 22", substrs=["Trace files"],
+ patterns=["pid: '2', tid: '21', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace",
+ "pid: '2', tid: '22', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace"])
+
+ # We switch targets and check the threads of this target
+ self.dbg.SetSelectedTarget(self.dbg.FindTargetWithProcessID(1))
+ self.expect("trace dump", substrs=["Trace files"],
+ patterns=["pid: '1', tid: '11', trace file: .*/test/API/commands/trace/intelpt-trace/3842849.trace"])
Index: lldb/source/Target/TraceSettingsParser.cpp
===================================================================
--- lldb/source/Target/TraceSettingsParser.cpp
+++ lldb/source/Target/TraceSettingsParser.cpp
@@ -166,10 +166,12 @@
return err;
}
+ for (auto target_sp : m_targets)
+ target_sp->SetTrace(m_trace.shared_from_this());
+
m_trace.m_settings = *raw_settings.getAsObject();
m_trace.m_settings_dir = m_settings_dir;
m_trace.m_thread_to_trace_file_map = m_thread_to_trace_file_map;
- m_trace.m_targets = m_targets;
return llvm::Error::success();
}
Index: lldb/source/Target/Trace.cpp
===================================================================
--- lldb/source/Target/Trace.cpp
+++ lldb/source/Target/Trace.cpp
@@ -13,6 +13,7 @@
#include "llvm/Support/Format.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
@@ -85,11 +86,7 @@
llvm::Error Trace::ParseSettings(Debugger &debugger,
const llvm::json::Value &settings,
llvm::StringRef settings_dir) {
- if (llvm::Error err =
- CreateParser()->ParseSettings(debugger, settings, settings_dir))
- return err;
-
- return llvm::Error::success();
+ return CreateParser()->ParseSettings(debugger, settings, settings_dir);
}
llvm::StringRef Trace::GetSchema() { return CreateParser()->GetSchema(); }
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -2965,6 +2965,31 @@
return error;
}
+void Target::SetTrace(const lldb::TraceSP &trace_sp) { m_trace_sp = trace_sp; }
+
+lldb::TraceSP &Target::GetTrace() { return m_trace_sp; }
+
+void Target::DumpTrace(Stream &s, TraceDumpOptions &options) {
+ if (!m_trace_sp) {
+ s << "error: no trace data in this target";
+ return;
+ }
+ if (!m_process_sp) {
+ s << "error: no process in this target";
+ return;
+ }
+ options.process = m_process_sp.get();
+ ThreadSP thread_sp;
+ for (lldb::tid_t tid : options.tids) {
+ if (!m_process_sp->GetThreadList().FindThreadByID(tid)) {
+ s.Printf("error: the thread id \"%" PRIu64
+ "\" does not exist in this target",
+ tid);
+ }
+ }
+ m_trace_sp->Dump(s, options);
+}
+
Status Target::Attach(ProcessAttachInfo &attach_info, Stream *stream) {
auto state = eStateInvalid;
auto process_sp = GetProcessSP();
Index: lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
+++ lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h
@@ -18,7 +18,8 @@
class TraceIntelPT : public lldb_private::Trace {
public:
- void Dump(lldb_private::Stream *s) const override;
+ void Dump(lldb_private::Stream &s,
+ const lldb_private::TraceDumpOptions &options) const override;
~TraceIntelPT() override = default;
@@ -43,6 +44,11 @@
std::unique_ptr<lldb_private::TraceSettingsParser> CreateParser() override;
private:
+ void DumpJSONSettings(lldb_private::Stream &s) const;
+
+ void DumpTraceFiles(lldb_private::Stream &s,
+ const lldb_private::TraceDumpOptions &options) const;
+
friend class TraceIntelPTSettingsParser;
pt_cpu m_pt_cpu;
};
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
@@ -8,8 +8,16 @@
#include "TraceIntelPT.h"
+#include <cassert>
+
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
+
#include "TraceIntelPTSettingsParser.h"
+#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/SectionLoadList.h"
using namespace lldb;
using namespace lldb_private;
@@ -44,7 +52,98 @@
uint32_t TraceIntelPT::GetPluginVersion() { return 1; }
-void TraceIntelPT::Dump(lldb_private::Stream *s) const {}
+void DumpImages(Stream &s, const TraceDumpOptions &options) {
+ s << "\n\nModules:";
+ s.IndentMore();
+
+ Target &target = options.process->GetTarget();
+ const ModuleList &modules = target.GetImages();
+
+ for (size_t i = 0; i < modules.GetSize(); i++) {
+ const ModuleSP module = modules.GetModuleAtIndex(i);
+ s << "\n";
+ s.Indent();
+ if (module->GetUUID().IsValid()) {
+ s << "uuid: ";
+ module->GetUUID().Dump(&s);
+ s << ", ";
+ }
+
+ if (ObjectFile *objfile = module->GetObjectFile()) {
+ Address base_addr(objfile->GetBaseAddress());
+ if (base_addr.IsValid()) {
+ s << "load address: ";
+ if (!target.GetSectionLoadList().IsEmpty()) {
+ lldb::addr_t load_addr = base_addr.GetLoadAddress(&target);
+ if (load_addr == LLDB_INVALID_ADDRESS) {
+ base_addr.Dump(&s, &target, Address::DumpStyleModuleWithFileAddress,
+ Address::DumpStyleFileAddress);
+ } else {
+ // Show the load address of the image
+ s.Printf("0x%" PRIx64, load_addr);
+ }
+ } else {
+ // The address was valid, but the image isn't loaded, output the
+ // address in an appropriate format
+ base_addr.Dump(&s, &target, Address::DumpStyleFileAddress);
+ }
+ s << ", ";
+ }
+ }
+ s << "file: ";
+ module->GetFileSpec().Dump(s.AsRawOstream());
+ }
+
+ s.IndentLess();
+}
+
+void TraceIntelPT::DumpJSONSettings(Stream &s) const {
+ s << "Settings:\n";
+ std::string str;
+ llvm::raw_string_ostream OS(str);
+ json::Object obj = m_settings;
+ OS << llvm::formatv("{0:2}", json::Value(std::move(obj)));
+ OS.flush();
+ s << OS.str();
+
+ s << "\n\nSettings directory:\n";
+ s.IndentMore();
+ s.Indent();
+ s << m_settings_dir << "\n\n";
+ s.IndentLess();
+}
+
+void TraceIntelPT::DumpTraceFiles(Stream &s,
+ const TraceDumpOptions &options) const {
+ s << "Trace files:";
+
+ lldb::pid_t pid = options.process->GetID();
+ const auto &process_it = m_thread_to_trace_file_map.find(pid);
+ if (process_it == m_thread_to_trace_file_map.end())
+ return;
+
+ s.IndentMore();
+ for (const auto &thread_trace_file : process_it->second) {
+ lldb::tid_t tid = thread_trace_file.first;
+ if (options.tids.empty() || options.tids.count(tid)) {
+ s << "\n";
+ s.Indent();
+ s.Printf("pid: '%" PRIu64 "', tid: '%" PRIu64 "', trace file: ", pid,
+ tid);
+ s << thread_trace_file.second;
+ }
+ }
+ s.IndentLess();
+}
+
+void TraceIntelPT::Dump(Stream &s, const TraceDumpOptions &options) const {
+ assert(options.process != nullptr && "The process field must be specified");
+ if (options.verbose) {
+ DumpJSONSettings(s);
+ }
+ DumpTraceFiles(s, options);
+ DumpImages(s, options);
+}
lldb::TraceSP TraceIntelPT::CreateInstance() {
return lldb::TraceSP(new TraceIntelPT());
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1188,6 +1188,8 @@
let Command = "trace dump" in {
def trace_dump_verbose : Option<"verbose", "v">, Group<1>,
Desc<"Show verbose trace information.">;
+ def trace_dump_thread_id : Option<"thread-id", "t">, Group<1>,
+ Arg<"ThreadID">, Desc<"The thread id to dump trace information of.">;
}
let Command = "trace schema" in {
Index: lldb/source/Commands/CommandObjectTrace.cpp
===================================================================
--- lldb/source/Commands/CommandObjectTrace.cpp
+++ lldb/source/Commands/CommandObjectTrace.cpp
@@ -147,7 +147,16 @@
switch (short_option) {
case 'v': {
- m_verbose = true;
+ m_dump_options.verbose = true;
+ break;
+ }
+ case 't': {
+ lldb::tid_t tid;
+ if (option_arg.empty() || option_arg.getAsInteger(0, tid))
+ error.SetErrorStringWithFormat("invalid thread id string '%s'",
+ option_arg.str().c_str());
+ else
+ m_dump_options.tids.insert(tid);
break;
}
default:
@@ -157,20 +166,21 @@
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
- m_verbose = false;
+ m_dump_options = TraceDumpOptions();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::makeArrayRef(g_trace_dump_options);
}
- bool m_verbose; // Enable verbose logging for debugging purposes.
+ TraceDumpOptions m_dump_options;
};
CommandObjectTraceDump(CommandInterpreter &interpreter)
- : CommandObjectParsed(interpreter, "trace dump",
- "Dump the loaded processor trace data.",
- "trace dump"),
+ : CommandObjectParsed(
+ interpreter, "trace dump",
+ "Dump the loaded processor trace data from the current target.",
+ "trace dump"),
m_options() {}
~CommandObjectTraceDump() override = default;
@@ -180,8 +190,11 @@
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
Status error;
- // TODO: fill in the dumping code here!
+ Target &target = GetSelectedOrDummyTarget();
+ target.DumpTrace(result.GetOutputStream(), m_options.m_dump_options);
+
if (error.Success()) {
+ result.AppendMessage("\n");
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendErrorWithFormat("%s\n", error.AsCString());
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -18,6 +18,17 @@
namespace lldb_private {
+/// Helper class that holds the configuration of a Dump action.
+///
+/// The \a process field must be set before invoking \a Trace::Dump.
+///
+/// If \a tids is empty, then all threads of the given process are considered.
+struct TraceDumpOptions {
+ Process *process = nullptr;
+ std::set<lldb::tid_t> tids;
+ bool verbose = false;
+};
+
/// \class Trace Trace.h "lldb/Target/Trace.h"
/// A plug-in interface definition class for trace information.
///
@@ -33,17 +44,21 @@
/// Processor trace information can also be fetched through the process
/// interfaces during a live debug session if your process supports gathering
/// this information.
-class Trace : public PluginInterface {
+class Trace : public PluginInterface,
+ public std::enable_shared_from_this<Trace> {
public:
/// Dump the trace data that this plug-in has access to.
///
/// This function will dump all of the trace data for all threads in a user
- /// readable format. Options for dumping can be added as this API is iterated
- /// on.
+ /// readable format.
///
/// \param[in] s
/// A stream object to dump the information to.
- virtual void Dump(Stream *s) const = 0;
+ ///
+ /// \param[in] options
+ /// The trace dump options. See \a TraceDumpOptions for more information.
+ virtual void Dump(lldb_private::Stream &s,
+ const TraceDumpOptions &options) const = 0;
/// Find a trace plug-in using JSON data.
///
@@ -135,7 +150,6 @@
std::map<lldb::pid_t, std::map<lldb::tid_t, lldb_private::FileSpec>>
m_thread_to_trace_file_map;
- std::vector<lldb::TargetSP> m_targets;
};
} // namespace lldb_private
Index: lldb/include/lldb/Target/Target.h
===================================================================
--- lldb/include/lldb/Target/Target.h
+++ lldb/include/lldb/Target/Target.h
@@ -29,6 +29,7 @@
#include "lldb/Target/PathMappingList.h"
#include "lldb/Target/SectionLoadHistory.h"
#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Trace.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Broadcaster.h"
#include "lldb/Utility/LLDBAssert.h"
@@ -210,7 +211,7 @@
bool GetInjectLocalVariables(ExecutionContext *exe_ctx) const;
void SetInjectLocalVariables(ExecutionContext *exe_ctx, bool b);
-
+
void SetRequireHardwareBreakpoints(bool b);
bool GetRequireHardwareBreakpoints() const;
@@ -1105,6 +1106,29 @@
void ClearAllLoadedSections();
+ /// Set the \a Trace object containing processor trace information of this
+ /// target.
+ ///
+ /// \param[in] trace_sp
+ /// The trace object.
+ void SetTrace(const lldb::TraceSP &trace_sp);
+
+ /// Get the \a Trace object containing processor trace information of this
+ /// target.
+ ///
+ /// \return
+ /// The trace object. It might be undefined.
+ lldb::TraceSP &GetTrace();
+
+ /// Dump the \a Trace information.
+ ///
+ /// \param[in] s
+ /// The stream object where the information is printed.
+ ///
+ /// \param[in] options
+ /// The trace dump options. See \a TraceDumpOptions for more information.
+ void DumpTrace(Stream &s, lldb_private::TraceDumpOptions &options);
+
// Since expressions results can persist beyond the lifetime of a process,
// and the const expression results are available after a process is gone, we
// provide a way for expressions to be evaluated from the Target itself. If
@@ -1234,7 +1258,7 @@
StructuredDataImpl *m_extra_args; // We own this structured data,
// but the SD itself manages the UP.
/// This holds the python callback object.
- StructuredData::GenericSP m_implementation_sp;
+ StructuredData::GenericSP m_implementation_sp;
/// Use CreateStopHook to make a new empty stop hook. The GetCommandPointer
/// and fill it with commands, and SetSpecifier to set the specifier shared
@@ -1354,12 +1378,12 @@
lldb::PlatformSP m_platform_sp; ///< The platform for this target.
std::recursive_mutex m_mutex; ///< An API mutex that is used by the lldb::SB*
/// classes make the SB interface thread safe
- /// When the private state thread calls SB API's - usually because it is
+ /// When the private state thread calls SB API's - usually because it is
/// running OS plugin or Python ThreadPlan code - it should not block on the
/// API mutex that is held by the code that kicked off the sequence of events
- /// that led us to run the code. We hand out this mutex instead when we
+ /// that led us to run the code. We hand out this mutex instead when we
/// detect that code is running on the private state thread.
- std::recursive_mutex m_private_mutex;
+ std::recursive_mutex m_private_mutex;
Arch m_arch;
ModuleList m_images; ///< The list of images for this process (shared
/// libraries and anything dynamically loaded).
@@ -1394,6 +1418,9 @@
bool m_suppress_stop_hooks;
bool m_is_dummy_target;
unsigned m_next_persistent_variable_index = 0;
+ /// An optional \a lldb_private::Trace object containing processor trace
+ /// information of this target.
+ lldb::TraceSP m_trace_sp;
/// Stores the frame recognizers of this target.
lldb::StackFrameRecognizerManagerUP m_frame_recognizer_manager_up;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits