hanbingwang created this revision.
hanbingwang added reviewers: wallace, clayborg.
Herald added a subscriber: dang.
hanbingwang requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
When the user types that command 'thread trace dump stats' and there's a
running Trace session in LLDB, a raw trace in bytes should be printed.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D105717
Files:
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/TestTraceDumpStats.py
lldb/test/API/commands/trace/TestTraceLoad.py
Index: lldb/test/API/commands/trace/TestTraceLoad.py
===================================================================
--- lldb/test/API/commands/trace/TestTraceLoad.py
+++ lldb/test/API/commands/trace/TestTraceLoad.py
@@ -33,7 +33,8 @@
# 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 stats", substrs=['''thread #1: tid = 3842849
+raw trace size 4096'''])
def testLoadInvalidTraces(self):
src_dir = self.getSourceDir()
Index: lldb/test/API/commands/trace/TestTraceDumpStats.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/trace/TestTraceDumpStats.py
@@ -0,0 +1,39 @@
+import lldb
+from intelpt_testcase import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.decorators import *
+
+class TestTraceDumpStats(TraceIntelPTTestCaseBase):
+ mydir = TestBase.compute_mydir(__file__)
+
+ def testErrorMessages(self):
+ # We first check the output when there are no targets
+ self.expect("thread trace dump stats",
+ 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 stats",
+ 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 stats",
+ 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 stats",
+ substrs=['''thread #1: tid = 3842849
+raw trace size 4096'''])
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
@@ -67,6 +67,10 @@
lldb::TraceCursorUP GetCursor(Thread &thread) override;
+ void DumpTraceStats(Thread &thread, Stream &s, bool verbose) override;
+
+ llvm::Optional<size_t> GetRawTraceSize(Thread &thread);
+
void DoRefreshLiveProcessState(
llvm::Expected<TraceGetStateResponse> state) override;
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
@@ -108,6 +108,24 @@
return Decode(thread)->GetCursor();
}
+void TraceIntelPT::DumpTraceStats(Thread &thread, Stream &s, bool verbose) {
+ Optional<size_t> raw_size = GetRawTraceSize(thread);
+ s.Printf("thread #%u: tid = %" PRIu64, thread.GetIndexID(), thread.GetID());
+ if (!raw_size) {
+ s.Printf(", not traced\n");
+ return;
+ }
+ s.Printf("\nraw trace size %zu\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)
Index: lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
+++ lldb/source/Plugins/Trace/intel-pt/IntelPTDecoder.cpp
@@ -194,7 +194,7 @@
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())
@@ -206,15 +206,17 @@
// 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));
@@ -233,11 +235,13 @@
: 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());
@@ -247,10 +251,11 @@
: 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());
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.h
@@ -121,7 +121,8 @@
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.
@@ -137,9 +138,16 @@
/// 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>;
Index: lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
===================================================================
--- lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
+++ lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp
@@ -50,6 +50,7 @@
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 {
@@ -92,8 +93,10 @@
}
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"));
Index: lldb/source/Commands/Options.td
===================================================================
--- lldb/source/Commands/Options.td
+++ lldb/source/Commands/Options.td
@@ -1065,6 +1065,11 @@
Desc<"Dump only instruction address without disassembly nor symbol information.">;
}
+let Command = "thread trace dump stats" in {
+ def thread_trace_dump_stats_verbose : Option<"verbose", "v">, Group<1>,
+ Desc<"show verbose thread trace dump stats">;
+}
+
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.">;
Index: lldb/source/Commands/CommandObjectThread.cpp
===================================================================
--- lldb/source/Commands/CommandObjectThread.cpp
+++ lldb/source/Commands/CommandObjectThread.cpp
@@ -2114,6 +2114,72 @@
std::map<const Thread *, TraceCursorUP> m_cursors;
};
+// CommandObjectTraceDumpStats
+#define LLDB_OPTIONS_thread_trace_dump_stats
+#include "CommandOptions.inc"
+
+class CommandObjectTraceDumpStats : 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_stats_options);
+ }
+
+ // Instance variables to hold the values for command options.
+ bool m_verbose;
+ };
+
+ CommandObjectTraceDumpStats(CommandInterpreter &interpreter)
+ : CommandObjectIterateOverThreads(
+ interpreter, "thread trace dump stats",
+ "Dump the traced stats for one thread.", nullptr,
+ eCommandRequiresProcess | eCommandTryTargetAPILock |
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused |
+ eCommandProcessMustBeTraced),
+ m_options() {}
+
+ ~CommandObjectTraceDumpStats() 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->DumpTraceStats(*thread_sp, result.GetOutputStream(),
+ m_options.m_verbose);
+ return true;
+ }
+
+ CommandOptions m_options;
+};
+
// CommandObjectMultiwordTraceDump
class CommandObjectMultiwordTraceDump : public CommandObjectMultiword {
public:
@@ -2126,6 +2192,8 @@
LoadSubCommand(
"instructions",
CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter)));
+ LoadSubCommand(
+ "stats", CommandObjectSP(new CommandObjectTraceDumpStats(interpreter)));
}
~CommandObjectMultiwordTraceDump() override = default;
};
Index: lldb/include/lldb/Target/Trace.h
===================================================================
--- lldb/include/lldb/Target/Trace.h
+++ lldb/include/lldb/Target/Trace.h
@@ -140,6 +140,20 @@
/// trace.
virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0;
+ /// Dump general stats 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 stats will be printed printed.
+ ///
+ /// \param[in] verbose
+ /// If \b true, print detailed stats
+ /// If \b false, print compact stats
+ virtual void DumpTraceStats(Thread &thread, Stream &s, bool verbose) = 0;
+
/// Check if a thread is currently traced by this object.
///
/// \param[in] thread
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits