Index: lldb/include/lldb/Target/Trace.h =================================================================== --- lldb/include/lldb/Target/Trace.h +++ lldb/include/lldb/Target/Trace.h @@ -140,6 +140,23 @@ /// 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 + /// + /// \param[in] plugin_name + /// The name of the tracing technology being used being used + virtual void DumpTraceInfo(Thread &thread, Stream &s, bool verbose, lldb_private::ConstString plugin_name) = 0; + /// Check if a thread is currently traced by this object. /// /// \param[in] thread Index: lldb/source/Commands/CommandObjectThread.cpp =================================================================== --- lldb/source/Commands/CommandObjectThread.cpp +++ lldb/source/Commands/CommandObjectThread.cpp @@ -32,6 +32,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/Trace.h" +#include "lldb/Utility/ConstString.h" #include "lldb/Utility/State.h" using namespace lldb; @@ -2114,6 +2115,72 @@ std::map m_cursors; }; +// 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 GetDefinitions() override { + return llvm::makeArrayRef(g_thread_trace_dump_info_options); + } + + // Instance variables to hold the values for command options. + bool m_verbose; + }; + + CommandObjectTraceDumpInfo(CommandInterpreter &interpreter) + : CommandObjectIterateOverThreads( + interpreter, "thread trace dump info", + "Dump the traced info for one thread.", 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, trace_sp->GetPluginName()); + return true; + } + + CommandOptions m_options; +}; + // CommandObjectMultiwordTraceDump class CommandObjectMultiwordTraceDump : public CommandObjectMultiword { public: @@ -2126,6 +2193,8 @@ LoadSubCommand( "instructions", CommandObjectSP(new CommandObjectTraceDumpInstructions(interpreter))); + LoadSubCommand( + "info", CommandObjectSP(new CommandObjectTraceDumpInfo(interpreter))); } ~CommandObjectMultiwordTraceDump() override = default; }; 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 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.">; 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 { public: DecodedThread(lldb::ThreadSP thread_sp, - std::vector &&instructions); + std::vector &&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 m_instructions; + size_t m_raw_trace_size; }; using DecodedThreadSP = std::shared_ptr; 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(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 &&instructions) - : m_thread_sp(thread_sp), m_instructions(std::move(instructions)) { + std::vector &&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/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> DecodeTraceFile(Process &process, TraceIntelPT &trace_intel_pt, - const FileSpec &trace_file) { + const FileSpec &trace_file, size_t &raw_trace_size) { ErrorOr> 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(const_cast(trace.getBufferStart())), trace.getBufferSize()); + raw_trace_size = trace_data.size(); return DecodeInMemoryTrace(process, trace_intel_pt, trace_data); } static Expected> -DecodeLiveThread(Thread &thread, TraceIntelPT &trace) { +DecodeLiveThread(Thread &thread, TraceIntelPT &trace, size_t &raw_trace_size) { Expected> buffer = trace.GetLiveThreadBuffer(thread.GetID()); if (!buffer) return buffer.takeError(); + raw_trace_size = buffer->size(); if (Expected cpu_info = trace.GetCPUInfo()) return DecodeInMemoryTrace(*thread.GetProcess(), trace, MutableArrayRef(*buffer)); @@ -233,11 +235,13 @@ : m_trace_thread(trace_thread), m_trace(trace) {} DecodedThreadSP PostMortemThreadDecoder::DoDecode() { + size_t raw_trace_size = 0; if (Expected> instructions = DecodeTraceFile(*m_trace_thread->GetProcess(), m_trace, - m_trace_thread->GetTraceFile())) + m_trace_thread->GetTraceFile(), raw_trace_size)) return std::make_shared(m_trace_thread->shared_from_this(), - std::move(*instructions)); + std::move(*instructions), + raw_trace_size); else return std::make_shared(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> instructions = - DecodeLiveThread(*m_thread_sp, m_trace)) - return std::make_shared(m_thread_sp, - std::move(*instructions)); + DecodeLiveThread(*m_thread_sp, m_trace, raw_trace_size)) + return std::make_shared( + m_thread_sp, std::move(*instructions), raw_trace_size); else return std::make_shared(m_thread_sp, instructions.takeError()); 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 DumpTraceInfo(Thread &thread, Stream &s, bool verbose, lldb_private::ConstString plugin_name) override; + + llvm::Optional GetRawTraceSize(Thread &thread); + void DoRefreshLiveProcessState( llvm::Expected 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::DumpTraceInfo(Thread &thread, Stream &s, bool verbose, lldb_private::ConstString plugin_name) { + Optional raw_size = GetRawTraceSize(thread); + s.Printf("Tracing technology: %s\nthread #%u: tid = %" PRIu64, plugin_name.AsCString(), thread.GetIndexID(), thread.GetID()); + if (!raw_size) { + s.Printf(", not traced\n"); + return; + } + s.Printf("\nRaw trace size: %zu bytes\n", *raw_size); + return; +} + +Optional TraceIntelPT::GetRawTraceSize(Thread &thread) { + if (IsTraced(thread)) + return Decode(thread)->GetRawTraceSize(); + else + return None; +} + Expected TraceIntelPT::GetCPUInfoForLiveProcess() { Expected> cpu_info = GetLiveProcessBinaryData("cpuInfo"); if (!cpu_info) Index: lldb/test/API/commands/trace/TestTraceDumpInfo.py =================================================================== --- /dev/null +++ lldb/test/API/commands/trace/TestTraceDumpInfo.py @@ -0,0 +1,40 @@ +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=['''Tracing technology: intel-pt +thread #1: tid = 3842849 +Raw trace size: 4096 bytes''']) 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,9 @@ # 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=['''Tracing technology: intel-pt +thread #1: tid = 3842849 +Raw trace size: 4096 bytes''']) def testLoadInvalidTraces(self): src_dir = self.getSourceDir()