[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