From a7d6c3effe93954ecad634eebbcc59fc6856c3d9 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Fri, 24 Jun 2022 14:02:47 -0700 Subject: [PATCH] [trace] Make events first class items in the trace cursor and rework errors We want to include events with metadata, like context switches, and this requires the API to handle events with payloads (e.g. information about such context switches). Besides this, we want to support multiple similar events between two consecutive instructions, like multiple context switches. However, the current implementation is not good for this because we are defining events as bitmask enums associated with specific instructions. Thus, we need to decouple instructions from events and make events actual items in the trace, just like instructions and errors. - Add accessors in the TraceCursor to know if an item is an event or not - Modify from the TraceDumper all the way to DecodedThread to support - Renamed the paused event to disabled. - Improved the tsc handling logic. I was using an API for getting the tsc from libipt, but that was an overkill that should be used when not processing events manually, but as we are already processing events, we can more easily get the tscs. event items. Fortunately this simplified many things - As part of this refactor, I also fixed and long stating issue, which is that some non decoding errors were being inserted in the decoded thread. I changed this so that TraceIntelPT::Decode returns an error if the decoder couldn't be set up proplerly. Then, errors within a trace are actual anomalies found in between instrutions. All test pass Differential Revision: https://reviews.llvm.org/D128576 --- lldb/include/lldb/Target/Trace.h | 6 +- lldb/include/lldb/Target/TraceCursor.h | 140 ++++--- .../{TraceInstructionDumper.h => TraceDumper.h} | 37 +- lldb/include/lldb/lldb-enumerations.h | 20 +- lldb/source/Commands/CommandObjectThread.cpp | 20 +- lldb/source/Commands/Options.td | 4 +- .../Plugins/Trace/intel-pt/DecodedThread.cpp | 161 +++----- lldb/source/Plugins/Trace/intel-pt/DecodedThread.h | 140 +++---- .../Plugins/Trace/intel-pt/LibiptDecoder.cpp | 164 +++------ lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h | 20 +- .../Plugins/Trace/intel-pt/ThreadDecoder.cpp | 42 ++- lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h | 4 +- .../Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp | 31 +- .../Plugins/Trace/intel-pt/TraceCursorIntelPT.h | 18 +- .../source/Plugins/Trace/intel-pt/TraceIntelPT.cpp | 51 +-- lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h | 7 +- .../Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp | 12 +- .../Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h | 5 +- .../Plugins/TraceExporter/common/TraceHTR.cpp | 11 +- .../ctf/CommandObjectThreadTraceExportCTF.cpp | 13 +- lldb/source/Target/CMakeLists.txt | 2 +- lldb/source/Target/TraceCursor.cpp | 34 +- ...{TraceInstructionDumper.cpp => TraceDumper.cpp} | 234 ++++++------ lldb/test/API/commands/trace/TestTraceDumpInfo.py | 11 +- .../commands/trace/TestTraceDumpInstructions.py | 406 +++++++++++---------- lldb/test/API/commands/trace/TestTraceEvents.py | 58 +-- lldb/test/API/commands/trace/TestTraceExport.py | 4 +- lldb/test/API/commands/trace/TestTraceLoad.py | 23 +- lldb/test/API/commands/trace/TestTraceStartStop.py | 18 +- lldb/test/API/commands/trace/TestTraceTSC.py | 16 +- .../TestTraceStartStopMultipleThreads.py | 10 +- 31 files changed, 789 insertions(+), 933 deletions(-) rename lldb/include/lldb/Target/{TraceInstructionDumper.h => TraceDumper.h} (78%) rename lldb/source/Target/{TraceInstructionDumper.cpp => TraceDumper.cpp} (59%) diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h index 6e06ceb..f4d7dee 100644 --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -167,9 +167,9 @@ public: /// /// \return /// A \a TraceCursorUP. If the thread is not traced or its trace - /// information failed to load, the corresponding error is embedded in the - /// trace. - virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0; + /// information failed to load, an \a llvm::Error is returned. + virtual llvm::Expected + CreateNewCursor(Thread &thread) = 0; /// Dump general info about a given thread's trace. Each Trace plug-in /// decides which data to show. diff --git a/lldb/include/lldb/Target/TraceCursor.h b/lldb/include/lldb/Target/TraceCursor.h index 7640fed..a4cf643 100644 --- a/lldb/include/lldb/Target/TraceCursor.h +++ b/lldb/include/lldb/Target/TraceCursor.h @@ -15,7 +15,8 @@ namespace lldb_private { -/// Class used for iterating over the instructions of a thread's trace. +/// Class used for iterating over the instructions of a thread's trace, among +/// other kinds of information. /// /// This class attempts to be a generic interface for accessing the instructions /// of the trace so that each Trace plug-in can reconstruct, represent and store @@ -23,21 +24,26 @@ namespace lldb_private { /// technology. /// /// Live processes: -/// In the case of a live process trace, an instance of a \a TraceCursor should -/// point to the trace at the moment it was collected. If the process is later -/// resumed and new trace data is collected, then it's up to each trace plug-in -/// to decide whether to leave the old cursor unaffected or not. +/// In the case of a live process trace, an instance of a \a TraceCursor +/// should point to the trace at the moment it was collected. If the process +/// is later resumed and new trace data is collected, then it's up to each +/// trace plug-in to decide whether to leave the old cursor unaffected or not. /// -/// Errors in the trace: -/// As there could be errors when reconstructing the instructions of a trace, -/// these errors are represented as failed instructions, and the cursor can -/// point at them. The consumer should invoke \a TraceCursor::IsError() to -/// check if the cursor is pointing to either a valid instruction or an error, -/// and then \a TraceCursor::GetError() can return the actual error message. +/// Cursor items: +/// A \a TraceCursor can point at one of the following items: /// -/// Instructions: -/// A \a TraceCursor always points to a specific instruction or error in the -/// trace. +/// Errors: +/// As there could be errors when reconstructing the instructions of a +/// trace, these errors are represented as failed instructions, and the +/// cursor can point at them. +/// +/// Events: +/// The cursor can also point at events in the trace, which aren't errors +/// nor instructions. An example of an event could be a context switch in +/// between two instructions. +/// +/// Instruction: +/// An actual instruction with a memory address. /// /// Defaults: /// By default, the cursor points at the most recent item in the trace and is @@ -49,17 +55,16 @@ namespace lldb_private { /// TraceCursorUP cursor = trace.GetTrace(thread); /// /// for (; cursor->HasValue(); cursor->Next()) { -/// if (cursor->IsError()) { -/// cout << "error found at: " << cursor->GetError() << endl; -/// continue; -/// } -/// -/// switch (cursor->GetInstructionControlFlowType()) { -/// eTraceInstructionControlFlowTypeCall: -/// std::cout << "CALL found at " << cursor->GetLoadAddress() << -/// std::endl; break; -/// eTraceInstructionControlFlowTypeReturn: -/// std::cout << "RETURN found at " << cursor->GetLoadAddress() << +/// TraceItemKind kind = cursor->GetItemKind(); +/// switch (cursor->GetItemKind()): +/// case eTraceItemKindError: +/// cout << "error found: " << cursor->GetError() << endl; +/// break; +/// case eTraceItemKindEvent: +/// cout << "event found: " << cursor->GetEventTypeAsString() << endl; +/// break; +/// case eTraceItemKindInstruction: +/// std::cout << "instructions found at " << cursor->GetLoadAddress() << /// std::endl; break; /// } /// } @@ -210,26 +215,45 @@ public: /// of this cursor. ExecutionContextRef &GetExecutionContextRef(); - /// Instruction or error information + /// Instruction, event or error information /// \{ /// \return + /// The kind of item the cursor is pointing at. + virtual lldb::TraceItemKind GetItemKind() const = 0; + + /// \return /// Whether the cursor points to an error or not. - virtual bool IsError() = 0; + bool IsError() const; + + /// \return + /// The error message the cursor is pointing at. + virtual const char *GetError() const = 0; + + /// \return + /// Whether the cursor points to an event or not. + bool IsEvent() const; + + /// \return + /// The specific kind of event the cursor is pointing at, or \b + /// TraceEvent::eTraceEventNone if the cursor not pointing to an event. + virtual lldb::TraceEvent GetEventType() const = 0; + + /// \return + /// A human-readable description of the event this cursor is pointing at. + const char *GetEventTypeAsString() const; + + /// \return + /// A human-readable description of the given event. + static const char *EventKindToString(lldb::TraceEvent event_kind); - /// Get the corresponding error message if the cursor points to an error in - /// the trace. - /// /// \return - /// \b nullptr if the cursor is not pointing to an error in - /// the trace. Otherwise return the actual error message. - virtual const char *GetError() = 0; + /// Whether the cursor points to an instruction. + bool IsInstruction() const; /// \return - /// The load address of the instruction the cursor is pointing at. If the - /// cursor points to an error in the trace, return \b - /// LLDB_INVALID_ADDRESS. - virtual lldb::addr_t GetLoadAddress() = 0; + /// The load address of the instruction the cursor is pointing at. + virtual lldb::addr_t GetLoadAddress() const = 0; /// Get the hardware counter of a given type associated with the current /// instruction. Each architecture might support different counters. It might @@ -240,52 +264,14 @@ public: /// The counter type. /// \return /// The value of the counter or \b llvm::None if not available. - virtual llvm::Optional GetCounter(lldb::TraceCounter counter_type) = 0; - - /// Get a bitmask with a list of events that happened chronologically right - /// before the current instruction or error, but after the previous - /// instruction. - /// - /// \return - /// The bitmask of events. - virtual lldb::TraceEvents GetEvents() = 0; - - /// \return - /// The \a lldb::TraceInstructionControlFlowType categories the - /// instruction the cursor is pointing at falls into. If the cursor points - /// to an error in the trace, return \b 0. - virtual lldb::TraceInstructionControlFlowType - GetInstructionControlFlowType() = 0; + virtual llvm::Optional + GetCounter(lldb::TraceCounter counter_type) const = 0; /// \} protected: ExecutionContextRef m_exe_ctx_ref; bool m_forwards = false; }; - -namespace trace_event_utils { -/// Convert an individual event to a display string. -/// -/// \param[in] event -/// An individual event. -/// -/// \return -/// A display string for that event, or nullptr if wrong data is passed -/// in. -const char *EventToDisplayString(lldb::TraceEvents event); - -/// Invoke the given callback for each individual event of the given events -/// bitmask. -/// -/// \param[in] events -/// A list of events to inspect. -/// -/// \param[in] callback -/// The callback to invoke for each event. -void ForEachEvent(lldb::TraceEvents events, - std::function callback); -} // namespace trace_event_utils - } // namespace lldb_private #endif // LLDB_TARGET_TRACE_CURSOR_H diff --git a/lldb/include/lldb/Target/TraceInstructionDumper.h b/lldb/include/lldb/Target/TraceDumper.h similarity index 78% rename from lldb/include/lldb/Target/TraceInstructionDumper.h rename to lldb/include/lldb/Target/TraceDumper.h index 03aa8d6..e78836e 100644 --- a/lldb/include/lldb/Target/TraceInstructionDumper.h +++ b/lldb/include/lldb/Target/TraceDumper.h @@ -1,4 +1,4 @@ -//===-- TraceInstructionDumper.h --------------------------------*- C++ -*-===// +//===-- TraceDumper.h -------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -15,9 +15,9 @@ namespace lldb_private { -/// Class that holds the configuration used by \a TraceInstructionDumper for +/// Class that holds the configuration used by \a TraceDumper for /// traversing and dumping instructions. -struct TraceInstructionDumperOptions { +struct TraceDumperOptions { /// If \b true, the cursor will be iterated forwards starting from the /// oldest instruction. Otherwise, the iteration starts from the most /// recent instruction. @@ -43,7 +43,7 @@ struct TraceInstructionDumperOptions { /// Class used to dump the instructions of a \a TraceCursor using its current /// state and granularity. -class TraceInstructionDumper { +class TraceDumper { public: /// Helper struct that holds symbol, disassembly and address information of an /// instruction. @@ -55,12 +55,13 @@ public: lldb_private::ExecutionContext exe_ctx; }; - /// Helper struct that holds all the information we know about an instruction - struct InstructionEntry { + /// Helper struct that holds all the information we know about a trace item + struct TraceItem { lldb::user_id_t id; lldb::addr_t load_address; llvm::Optional tsc; llvm::Optional error; + llvm::Optional event; llvm::Optional symbol_info; llvm::Optional prev_symbol_info; }; @@ -71,14 +72,11 @@ public: public: virtual ~OutputWriter() = default; - /// Indicate a user-level info message. It's not part of the actual trace. - virtual void InfoMessage(llvm::StringRef text) {} + /// Notify this writer that the cursor ran out of data. + virtual void NoMoreData() {} - /// Dump a trace event. - virtual void Event(llvm::StringRef text) = 0; - - /// Dump an instruction or a trace error. - virtual void Instruction(const InstructionEntry &insn) = 0; + /// Dump a trace item (instruction, error or event). + virtual void TraceItem(const TraceItem &item) = 0; }; /// Create a instruction dumper for the cursor. @@ -91,8 +89,8 @@ public: /// /// \param[in] options /// Additional options for configuring the dumping. - TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, Stream &s, - const TraceInstructionDumperOptions &options); + TraceDumper(lldb::TraceCursorUP &&cursor_up, Stream &s, + const TraceDumperOptions &options); /// Dump \a count instructions of the thread trace starting at the current /// cursor position. @@ -109,14 +107,11 @@ public: llvm::Optional DumpInstructions(size_t count); private: - /// Create an instruction entry for the current position without symbol - /// information. - InstructionEntry CreatRawInstructionEntry(); - - void PrintEvents(); + /// Create a trace item for the current position without symbol information. + TraceItem CreatRawTraceItem(); lldb::TraceCursorUP m_cursor_up; - TraceInstructionDumperOptions m_options; + TraceDumperOptions m_options; std::unique_ptr m_writer_up; }; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index e1e3332..eba2667 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -1150,17 +1150,23 @@ enum SaveCoreStyle { // Type of counter values associated with instructions in a trace. enum TraceCounter { // Timestamp counter, like the one offered by Intel CPUs (TSC). - eTraceCounterTSC, + eTraceCounterTSC = 0, }; // Events that might happen during a trace session. -FLAGS_ENUM(TraceEvents){ - // Tracing was paused. If instructions were executed after pausing - // and before resuming, the TraceCursor used to traverse the trace - // should provide an error signalinig this data loss. - eTraceEventPaused = (1u << 0), +enum TraceEvent { + // Tracing was disabled for some time due to a software trigger + eTraceEventDisabledSW, + // Tracing was disable for some time due to a hardware trigger + eTraceEventDisabledHW, +}; + +// Enum used to identify which kind of item a \a TraceCursor is pointing at +enum TraceItemKind { + eTraceItemKindError = 0, + eTraceItemKindEvent, + eTraceItemKindInstruction, }; -LLDB_MARK_AS_BITMASK_ENUM(TraceEvents) } // namespace lldb diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp index 7dd89e6..993523e 100644 --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -33,7 +33,7 @@ #include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlanStepInRange.h" #include "lldb/Target/Trace.h" -#include "lldb/Target/TraceInstructionDumper.h" +#include "lldb/Target/TraceDumper.h" #include "lldb/Utility/State.h" using namespace lldb; @@ -2208,7 +2208,7 @@ public: size_t m_count; size_t m_continue; llvm::Optional m_output_file; - TraceInstructionDumperOptions m_dumper_options; + TraceDumperOptions m_dumper_options; }; CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter) @@ -2272,8 +2272,14 @@ protected: m_options.m_dumper_options.id = m_last_id; } - TraceCursorUP cursor_up = - m_exe_ctx.GetTargetSP()->GetTrace()->GetCursor(*thread_sp); + llvm::Expected cursor_or_error = + m_exe_ctx.GetTargetSP()->GetTrace()->CreateNewCursor(*thread_sp); + + if (!cursor_or_error) { + result.AppendError(llvm::toString(cursor_or_error.takeError())); + return false; + } + TraceCursorUP &cursor_up = *cursor_or_error; if (m_options.m_dumper_options.id && !cursor_up->HasId(*m_options.m_dumper_options.id)) { @@ -2295,9 +2301,9 @@ protected: cursor_up->Seek(1, TraceCursor::SeekType::End); } - TraceInstructionDumper dumper( - std::move(cursor_up), out_file ? *out_file : result.GetOutputStream(), - m_options.m_dumper_options); + TraceDumper dumper(std::move(cursor_up), + out_file ? *out_file : result.GetOutputStream(), + m_options.m_dumper_options); m_last_id = dumper.DumpInstructions(m_options.m_count); return true; diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index 6e5f1ef..7755daa 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -1136,8 +1136,8 @@ let Command = "thread trace dump instructions" in { "id can be provided in decimal or hexadecimal representation.">; def thread_trace_dump_instructions_skip: Option<"skip", "s">, Group<1>, Arg<"Index">, - Desc<"How many instruction to skip from the starting position of the trace " - "before starting the traversal.">; + Desc<"How many trace items (instructions, errors and events) to skip from " + "the starting position of the trace before starting the traversal.">; def thread_trace_dump_instructions_raw : Option<"raw", "r">, Group<1>, Desc<"Dump only instruction address without disassembly nor symbol " "information.">; diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp index e5d8e2d..578828f 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.cpp @@ -39,114 +39,59 @@ IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address) } void IntelPTError::log(llvm::raw_ostream &OS) const { - const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code)); - if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) { - write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18); - OS << " "; - } - OS << "error: " << libipt_error_message; -} - -DecodedInstruction::operator bool() const { - return !IsLibiptError(libipt_error); + OS << pt_errstr(pt_errcode(m_libipt_error_code)); + if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) + OS << formatv(": {0:x+16}", m_address); } -size_t DecodedThread::GetInstructionsCount() const { - return m_instruction_ips.size(); +int64_t DecodedThread::GetItemsCount() const { + return static_cast(m_item_kinds.size()); } -lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t insn_index) const { - return m_instruction_ips[insn_index]; +lldb::addr_t DecodedThread::GetInstructionLoadAddress(size_t item_index) const { + return m_item_data[item_index].load_address; } -TraceInstructionControlFlowType -DecodedThread::GetInstructionControlFlowType(size_t insn_index) const { - if (IsInstructionAnError(insn_index)) - return (TraceInstructionControlFlowType)0; - - TraceInstructionControlFlowType mask = - eTraceInstructionControlFlowTypeInstruction; - - lldb::addr_t load_address = m_instruction_ips[insn_index]; - uint8_t insn_byte_size = m_instruction_sizes[insn_index]; - pt_insn_class iclass = m_instruction_classes[insn_index]; - - switch (iclass) { - case ptic_cond_jump: - case ptic_jump: - case ptic_far_jump: - mask |= eTraceInstructionControlFlowTypeBranch; - if (insn_index + 1 < m_instruction_ips.size() && - load_address + insn_byte_size != m_instruction_ips[insn_index + 1]) - mask |= eTraceInstructionControlFlowTypeTakenBranch; - break; - case ptic_return: - case ptic_far_return: - mask |= eTraceInstructionControlFlowTypeReturn; - break; - case ptic_call: - case ptic_far_call: - mask |= eTraceInstructionControlFlowTypeCall; - break; - default: - break; - } +ThreadSP DecodedThread::GetThread() { return m_thread_sp; } - return mask; +DecodedThread::TraceItemStorage & +DecodedThread::CreateNewTraceItem(lldb::TraceItemKind kind) { + m_item_kinds.push_back(kind); + m_item_data.emplace_back(); + return m_item_data.back(); } -ThreadSP DecodedThread::GetThread() { return m_thread_sp; } - -void DecodedThread::RecordTscForLastInstruction(uint64_t tsc) { +void DecodedThread::NotifyTsc(uint64_t tsc) { if (!m_last_tsc || *m_last_tsc != tsc) { - // In case the first instructions are errors or did not have a TSC, we'll - // get a first valid TSC not in position 0. We can safely force these error - // instructions to use the first valid TSC, so that all the trace has TSCs. - size_t start_index = - m_instruction_timestamps.empty() ? 0 : m_instruction_ips.size() - 1; - m_instruction_timestamps.emplace(start_index, tsc); + m_instruction_timestamps.emplace(m_item_kinds.size(), tsc); m_last_tsc = tsc; } } -void DecodedThread::Append(const DecodedInstruction &insn) { - if (!insn) { - // End of stream shouldn't be a public error - if (IsEndOfStream(insn.libipt_error)) - return; - - AppendError(make_error(insn.libipt_error, insn.pt_insn.ip)); - } else { - m_instruction_ips.emplace_back(insn.pt_insn.ip); - m_instruction_sizes.emplace_back(insn.pt_insn.size); - m_instruction_classes.emplace_back(insn.pt_insn.iclass); - } - - if (insn.tsc) - RecordTscForLastInstruction(*insn.tsc); +void DecodedThread::AppendEvent(lldb::TraceEvent event) { + CreateNewTraceItem(lldb::eTraceItemKindEvent).event = event; + m_events_stats.RecordEvent(event); +} - if (insn.events) { - m_events.try_emplace(m_instruction_ips.size() - 1, insn.events); - m_events_stats.RecordEventsForInstruction(insn.events); - } +void DecodedThread::AppendInstruction(const pt_insn &insn) { + CreateNewTraceItem(lldb::eTraceItemKindInstruction).load_address = insn.ip; } -void DecodedThread::AppendError(llvm::Error &&error) { - m_errors.try_emplace(m_instruction_ips.size(), toString(std::move(error))); - m_instruction_ips.emplace_back(LLDB_INVALID_ADDRESS); - m_instruction_sizes.emplace_back(0); - m_instruction_classes.emplace_back(pt_insn_class::ptic_error); +void DecodedThread::AppendError(const IntelPTError &error) { + // End of stream shouldn't be a public error + if (IsEndOfStream(error.GetLibiptErrorCode())) + return; + CreateNewTraceItem(lldb::eTraceItemKindError).error = + ConstString(error.message()).AsCString(); } -void DecodedThread::SetAsFailed(llvm::Error &&error) { - AppendError(std::move(error)); +void DecodedThread::AppendCustomError(StringRef err) { + CreateNewTraceItem(lldb::eTraceItemKindError).error = + ConstString(err).AsCString(); } -lldb::TraceEvents DecodedThread::GetEvents(int insn_index) { - auto it = m_events.find(insn_index); - if (it != m_events.end()) - return it->second; - return (TraceEvents)0; +lldb::TraceEvent DecodedThread::GetEventByIndex(int item_index) const { + return m_item_data[item_index].event; } void DecodedThread::LibiptErrorsStats::RecordError(int libipt_error_code) { @@ -167,16 +112,9 @@ const DecodedThread::EventsStats &DecodedThread::GetEventsStats() const { return m_events_stats; } -void DecodedThread::EventsStats::RecordEventsForInstruction( - lldb::TraceEvents events) { - if (!events) - return; - - total_instructions_with_events++; - trace_event_utils::ForEachEvent(events, [&](TraceEvents event) { - events_counts[event]++; - total_count++; - }); +void DecodedThread::EventsStats::RecordEvent(lldb::TraceEvent event) { + events_counts[event]++; + total_count++; } Optional DecodedThread::CalculateTscRange( @@ -205,35 +143,24 @@ Optional DecodedThread::CalculateTscRange( return TscRange(--it, *this); } -bool DecodedThread::IsInstructionAnError(size_t insn_idx) const { - return m_instruction_ips[insn_idx] == LLDB_INVALID_ADDRESS; +lldb::TraceItemKind DecodedThread::GetItemKindByIndex(size_t item_index) const { + return static_cast(m_item_kinds[item_index]); } -const char *DecodedThread::GetErrorByInstructionIndex(size_t insn_idx) { - auto it = m_errors.find(insn_idx); - if (it == m_errors.end()) - return nullptr; - - return it->second.c_str(); +const char *DecodedThread::GetErrorByIndex(size_t item_index) const { + return m_item_data[item_index].error; } DecodedThread::DecodedThread(ThreadSP thread_sp) : m_thread_sp(thread_sp) {} -DecodedThread::DecodedThread(ThreadSP thread_sp, Error &&error) - : m_thread_sp(thread_sp) { - AppendError(std::move(error)); -} - -lldb::TraceCursorUP DecodedThread::GetCursor() { +lldb::TraceCursorUP DecodedThread::CreateNewCursor() { return std::make_unique(m_thread_sp, shared_from_this()); } size_t DecodedThread::CalculateApproximateMemoryUsage() const { - return sizeof(pt_insn::ip) * m_instruction_ips.size() + - sizeof(pt_insn::size) * m_instruction_sizes.size() + - sizeof(pt_insn::iclass) * m_instruction_classes.size() + - (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size() + - m_errors.getMemorySize() + m_events.getMemorySize(); + return sizeof(TraceItemStorage) * m_item_data.size() + + sizeof(uint8_t) * m_item_kinds.size() + + (sizeof(size_t) + sizeof(uint64_t)) * m_instruction_timestamps.size(); } DecodedThread::TscRange::TscRange(std::map::const_iterator it, @@ -242,7 +169,7 @@ DecodedThread::TscRange::TscRange(std::map::const_iterator it, auto next_it = m_it; ++next_it; m_end_index = (next_it == m_decoded_thread->m_instruction_timestamps.end()) - ? m_decoded_thread->GetInstructionsCount() - 1 + ? std::numeric_limits::max() : next_it->first - 1; } diff --git a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h index 3899c43..b17e927 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -53,6 +53,8 @@ public: return llvm::errc::not_supported; } + int GetLibiptErrorCode() const { return m_libipt_error_code; } + void log(llvm::raw_ostream &OS) const override; private: @@ -60,27 +62,6 @@ private: lldb::addr_t m_address; }; -/// Helper struct for building an instruction or error from the decoder. -/// It holds associated events and timing information. -struct DecodedInstruction { - DecodedInstruction() { - pt_insn.ip = LLDB_INVALID_ADDRESS; - libipt_error = pte_ok; - } - - DecodedInstruction(int libipt_error_code) : DecodedInstruction() { - libipt_error = libipt_error_code; - } - - /// \return \b true if and only if this struct holds a libipt error. - explicit operator bool() const; - - int libipt_error; - lldb::TraceEvents events = (lldb::TraceEvents)0; - llvm::Optional tsc = llvm::None; - pt_insn pt_insn; -}; - /// \class DecodedThread /// Class holding the instructions and function call hierarchy obtained from /// decoding a trace, as well as a position cursor used when reverse debugging @@ -143,11 +124,10 @@ public: struct EventsStats { /// A count for each individual event kind. We use an unordered map instead /// of a DenseMap because DenseMap can't understand enums. - std::unordered_map events_counts; + std::unordered_map events_counts; size_t total_count = 0; - size_t total_instructions_with_events = 0; - void RecordEventsForInstruction(lldb::TraceEvents events); + void RecordEvent(lldb::TraceEvent event); }; DecodedThread(lldb::ThreadSP thread_sp); @@ -155,35 +135,9 @@ public: /// Utility constructor that initializes the trace with a provided error. DecodedThread(lldb::ThreadSP thread_sp, llvm::Error &&err); - /// Append an instruction or a libipt error. - void Append(const DecodedInstruction &insn); - - /// Append an error signaling that decoding completely failed. - void SetAsFailed(llvm::Error &&error); - - /// Get a bitmask with the events that happened chronologically right before - /// the instruction pointed by the given instruction index, but after the - /// previous instruction. - lldb::TraceEvents GetEvents(int insn_index); - - /// Get the total number of instruction pointers from the decoded trace. - /// This will include instructions that indicate errors (or gaps) in the - /// trace. For an instruction error, you can access its underlying error - /// message with the \a GetErrorByInstructionIndex() method. - size_t GetInstructionsCount() const; - - /// \return - /// The load address of the instruction at the given index, or \a - /// LLDB_INVALID_ADDRESS if it is an error. - lldb::addr_t GetInstructionLoadAddress(size_t insn_index) const; - - /// Get the \a lldb::TraceInstructionControlFlowType categories of the - /// instruction. - /// - /// \return - /// The control flow categories, or \b 0 if the instruction is an error. - lldb::TraceInstructionControlFlowType - GetInstructionControlFlowType(size_t insn_index) const; + /// Get the total number of instruction, errors and events from the decoded + /// trace. + int64_t GetItemsCount() const; /// Construct the TSC range that covers the given instruction index. /// This operation is O(logn) and should be used sparingly. @@ -202,18 +156,24 @@ public: size_t insn_index, const llvm::Optional &hint_range) const; - /// Check if an instruction given by its index is an error. - bool IsInstructionAnError(size_t insn_idx) const; + /// \return + /// The error associated with a given trace item. + const char *GetErrorByIndex(size_t item_index) const; - /// Get the error associated with a given instruction index. - /// /// \return - /// The error message of \b nullptr if the given index - /// points to a valid instruction. - const char *GetErrorByInstructionIndex(size_t ins_idx); + /// The trace item kind given an item index. + lldb::TraceItemKind GetItemKindByIndex(size_t item_index) const; + + /// \return + /// The underlying event type for the given trace item index. + lldb::TraceEvent GetEventByIndex(int item_index) const; + + /// \return + /// The load address of the instruction at the given index. + lldb::addr_t GetInstructionLoadAddress(size_t item_index) const; /// Get a new cursor for the decoded thread. - lldb::TraceCursorUP GetCursor(); + lldb::TraceCursorUP CreateNewCursor(); /// Return an object with statistics of the TSC decoding errors that happened. /// A TSC error is not a fatal error and doesn't create gaps in the trace. @@ -243,24 +203,50 @@ public: lldb::ThreadSP GetThread(); - /// Append a decoding error given an llvm::Error. - void AppendError(llvm::Error &&error); + /// Notify this object that a new tsc has been seen. + void NotifyTsc(uint64_t tsc); -private: - /// Notify this class that the last added instruction or error has - /// an associated TSC. - void RecordTscForLastInstruction(uint64_t tsc); + /// Append a decoding error. + void AppendError(const IntelPTError &error); + /// Append a custom decoding. + void AppendCustomError(llvm::StringRef error); + + /// Append an event. + void AppendEvent(lldb::TraceEvent); + + /// Append an instruction. + void AppendInstruction(const pt_insn &insn); + +private: /// When adding new members to this class, make sure /// to update \a CalculateApproximateMemoryUsage() accordingly. lldb::ThreadSP m_thread_sp; - /// The low level storage of all instruction addresses. Each instruction has - /// an index in this vector and it will be used in other parts of the code. - std::vector m_instruction_ips; - /// The size in bytes of each instruction. - std::vector m_instruction_sizes; - /// The libipt instruction class for each instruction. - std::vector m_instruction_classes; + + /// We use a union to optimize the memory usage for the different kinds of + /// trace items. + union TraceItemStorage { + /// The load addresses of this item if it's an instruction. + uint64_t load_address; + + /// The event kind of this item if it's an event + lldb::TraceEvent event; + + /// The string message of this item if it's an error + const char *error; + }; + + /// Create a new trace item. + /// + /// \return + /// The index of the new item. + DecodedThread::TraceItemStorage &CreateNewTraceItem(lldb::TraceItemKind kind); + + /// Most of the trace data is stored here. + std::vector m_item_data; + /// The TraceItemKind for each trace item encoded as uint8_t. We don't include + /// it in TraceItemStorage to avoid padding. + std::vector m_item_kinds; /// This map contains the TSCs of the decoded instructions. It maps /// `instruction index -> TSC`, where `instruction index` is the first index @@ -271,12 +257,6 @@ private: std::map m_instruction_timestamps; /// This is the chronologically last TSC that has been added. llvm::Optional m_last_tsc = llvm::None; - // This variables stores the messages of all the error instructions in the - // trace. It maps `instruction index -> error message`. - llvm::DenseMap m_errors; - /// This variable stores the bitmask of events that happened right before - /// the instruction given as a key. It maps `instruction index -> events`. - llvm::DenseMap m_events; /// Statistics of all tracing events. EventsStats m_events_stats; diff --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp index c17ef00..dd34467 100644 --- a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.cpp @@ -16,16 +16,6 @@ using namespace lldb_private; using namespace lldb_private::trace_intel_pt; using namespace llvm; -// Simple struct used by the decoder to keep the state of the most -// recent TSC and a flag indicating whether TSCs are enabled, not enabled -// or we just don't yet. -struct TscInfo { - uint64_t tsc = 0; - LazyBool has_tsc = eLazyBoolCalculate; - - explicit operator bool() const { return has_tsc == eLazyBoolYes; } -}; - /// Class that decodes a raw buffer for a single thread using the low level /// libipt library. /// @@ -58,7 +48,7 @@ public: int status = pt_insn_sync_forward(&m_decoder); if (IsLibiptError(status)) { - m_decoded_thread.Append(DecodedInstruction(status)); + m_decoded_thread.AppendError(IntelPTError(status)); break; } @@ -71,25 +61,13 @@ public: void DecodePSB(uint64_t psb_offset) { int status = pt_insn_sync_set(&m_decoder, psb_offset); if (IsLibiptError(status)) { - m_decoded_thread.Append(DecodedInstruction(status)); + m_decoded_thread.AppendError(IntelPTError(status)); return; } DecodeInstructionsAndEvents(status, /*stop_on_psb_change=*/true); } private: - /// Invoke the low level function \a pt_insn_next and store the decoded - /// instruction in the given \a DecodedInstruction. - /// - /// \param[out] insn - /// The instruction builder where the pt_insn information will be stored. - /// - /// \return - /// The status returned by pt_insn_next. - int DecodeNextInstruction(DecodedInstruction &insn) { - return pt_insn_next(&m_decoder, &insn.pt_insn, sizeof(insn.pt_insn)); - } - /// Decode all the instructions and events until an error is found, the end /// of the trace is reached, or optionally a new PSB is reached. /// @@ -104,29 +82,25 @@ private: pt_insn_get_sync_offset(&m_decoder, &psb_offset); // this can't fail because we got here - while (true) { - DecodedInstruction insn = ProcessPTEvents(status); - if (!insn) { - m_decoded_thread.Append(insn); - break; - } - + while (ProcessPTEvents(status)) { if (stop_on_psb_change) { uint64_t cur_psb_offset; - pt_insn_get_sync_offset( - &m_decoder, &cur_psb_offset); // this can't fail because we got here + // this can't fail because we got here + pt_insn_get_sync_offset(&m_decoder, &cur_psb_offset); if (cur_psb_offset != psb_offset) break; } - // The status returned by DecodeNextInstruction will need to be processed + // The status returned by pt_insn_next will need to be processed // by ProcessPTEvents in the next loop if it is not an error. - if (IsLibiptError(status = DecodeNextInstruction(insn))) { - insn.libipt_error = status; - m_decoded_thread.Append(insn); + pt_insn insn; + std::memset(&insn, 0, sizeof insn); + if (IsLibiptError(status = + pt_insn_next(&m_decoder, &insn, sizeof(insn)))) { + m_decoded_thread.AppendError(IntelPTError(status, insn.ip)); break; } - m_decoded_thread.Append(insn); + m_decoded_thread.AppendInstruction(insn); } } @@ -151,7 +125,7 @@ private: // We make this call to record any synchronization errors. if (IsLibiptError(status)) - m_decoded_thread.Append(DecodedInstruction(status)); + m_decoded_thread.AppendError(IntelPTError(status)); return status; } @@ -165,86 +139,49 @@ private: /// synchronization. /// /// \return - /// A \a DecodedInstruction with event, tsc and error information. - DecodedInstruction ProcessPTEvents(int status) { - DecodedInstruction insn; + /// \b true if no errors were found processing the events. + bool ProcessPTEvents(int status) { while (status & pts_event_pending) { pt_event event; status = pt_insn_event(&m_decoder, &event, sizeof(event)); if (IsLibiptError(status)) { - insn.libipt_error = status; - break; + m_decoded_thread.AppendError(IntelPTError(status)); + return false; } + if (event.has_tsc) + m_decoded_thread.NotifyTsc(event.tsc); + switch (event.type) { case ptev_enabled: // The kernel started or resumed tracing the program. break; case ptev_disabled: // The CPU paused tracing the program, e.g. due to ip filtering. + m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledHW); + break; case ptev_async_disabled: // The kernel or user code paused tracing the program, e.g. // a breakpoint or a ioctl invocation pausing the trace, or a // context switch happened. - - if (m_decoded_thread.GetInstructionsCount() > 0) { - // A paused event before the first instruction can be safely - // discarded. - insn.events |= eTraceEventPaused; - } + m_decoded_thread.AppendEvent(lldb::eTraceEventDisabledSW); break; case ptev_overflow: // The CPU internal buffer had an overflow error and some instructions // were lost. - insn.libipt_error = -pte_overflow; + m_decoded_thread.AppendError(IntelPTError(-pte_overflow)); break; default: break; } } - // We refresh the TSC that might have changed after processing the events. - // See - // https://github.com/intel/libipt/blob/master/doc/man/pt_evt_next.3.md - RefreshTscInfo(); - if (m_tsc_info) - insn.tsc = m_tsc_info.tsc; - return insn; - } - - /// Query the decoder for the most recent TSC timestamp and update - /// the inner tsc information accordingly. - void RefreshTscInfo() { - if (m_tsc_info.has_tsc == eLazyBoolNo) - return; - - uint64_t new_tsc; - int tsc_status; - if (IsLibiptError(tsc_status = pt_insn_time(&m_decoder, &new_tsc, nullptr, - nullptr))) { - if (IsTscUnavailable(tsc_status)) { - // We now know that the trace doesn't support TSC, so we won't try - // again. - // See - // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_time.3.md - m_tsc_info.has_tsc = eLazyBoolNo; - } else { - // We don't add TSC decoding errors in the decoded trace itself to - // prevent creating unnecessary gaps, but we can count how many of - // these errors happened. In this case we reuse the previous correct - // TSC we saw, as it's better than no TSC at all. - m_decoded_thread.RecordTscError(tsc_status); - } - } else { - m_tsc_info.tsc = new_tsc; - m_tsc_info.has_tsc = eLazyBoolYes; - } + return true; } private: pt_insn_decoder &m_decoder; DecodedThread &m_decoded_thread; - TscInfo m_tsc_info; }; /// Callback used by libipt for reading the process memory. @@ -308,23 +245,24 @@ static Error SetupMemoryImage(PtInsnDecoderUP &decoder_up, Process &process) { return Error::success(); } -void lldb_private::trace_intel_pt::DecodeSingleTraceForThread(DecodedThread &decoded_thread, - TraceIntelPT &trace_intel_pt, - ArrayRef buffer) { +Error lldb_private::trace_intel_pt::DecodeSingleTraceForThread( + DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, + ArrayRef buffer) { Expected decoder_up = CreateInstructionDecoder(trace_intel_pt, buffer); if (!decoder_up) - return decoded_thread.SetAsFailed(decoder_up.takeError()); + return decoder_up.takeError(); if (Error err = SetupMemoryImage(*decoder_up, *decoded_thread.GetThread()->GetProcess())) - return decoded_thread.SetAsFailed(std::move(err)); + return err; LibiptDecoder libipt_decoder(*decoder_up.get(), decoded_thread); libipt_decoder.DecodeUntilEndOfTrace(); + return Error::success(); } -void lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( +Error lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, const DenseMap> &buffers, const std::vector &executions) { @@ -333,11 +271,11 @@ void lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( Expected decoder_up = CreateInstructionDecoder(trace_intel_pt, cpu_id_buffer.second); if (!decoder_up) - return decoded_thread.SetAsFailed(decoder_up.takeError()); + return decoder_up.takeError(); if (Error err = SetupMemoryImage(*decoder_up, *decoded_thread.GetThread()->GetProcess())) - return decoded_thread.SetAsFailed(std::move(err)); + return err; decoders.try_emplace(cpu_id_buffer.first, LibiptDecoder(*decoder_up->release(), decoded_thread)); @@ -351,24 +289,22 @@ void lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( // If we haven't seen a PSB yet, then it's fine not to show errors if (has_seen_psbs) { if (execution.intelpt_subtraces.empty()) { - decoded_thread.AppendError(createStringError( - inconvertibleErrorCode(), - formatv("Unable to find intel pt data for thread execution with " - "tsc = {0} on cpu id = {1}", - execution.thread_execution.GetLowestKnownTSC(), - execution.thread_execution.cpu_id))); + decoded_thread.AppendCustomError( + formatv("Unable to find intel pt data for thread " + "execution on cpu id = {0}", + execution.thread_execution.cpu_id) + .str()); } // If the first execution is incomplete because it doesn't have a previous // context switch in its cpu, all good. if (variant == ThreadContinuousExecution::Variant::OnlyEnd || variant == ThreadContinuousExecution::Variant::HintedStart) { - decoded_thread.AppendError(createStringError( - inconvertibleErrorCode(), - formatv("Thread execution starting at tsc = {0} on cpu id = {1} " - "doesn't have a matching context switch in.", - execution.thread_execution.GetLowestKnownTSC(), - execution.thread_execution.cpu_id))); + decoded_thread.AppendCustomError( + formatv("Thread execution starting on cpu id = {0} doesn't " + "have a matching context switch in.", + execution.thread_execution.cpu_id) + .str()); } } @@ -387,15 +323,15 @@ void lldb_private::trace_intel_pt::DecodeSystemWideTraceForThread( if ((variant == ThreadContinuousExecution::Variant::OnlyStart && i + 1 != executions.size()) || variant == ThreadContinuousExecution::Variant::HintedEnd) { - decoded_thread.AppendError(createStringError( - inconvertibleErrorCode(), - formatv("Thread execution starting at tsc = {0} on cpu id = {1} " - "doesn't have a matching context switch out", - execution.thread_execution.GetLowestKnownTSC(), - execution.thread_execution.cpu_id))); + decoded_thread.AppendCustomError( + formatv("Thread execution on cpu id = {0} doesn't have a " + "matching context switch out", + execution.thread_execution.cpu_id) + .str()); } } } + return Error::success(); } bool IntelPTThreadContinousExecution::operator<( diff --git a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h index 5420c03..cad4d39 100644 --- a/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/LibiptDecoder.h @@ -42,11 +42,15 @@ struct IntelPTThreadContinousExecution { bool operator<(const IntelPTThreadContinousExecution &o) const; }; -/// Decode a raw Intel PT trace for a single thread given in \p buffer and append the decoded -/// instructions and errors in \p decoded_thread. It uses the low level libipt -/// library underneath. -void DecodeSingleTraceForThread(DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, - llvm::ArrayRef buffer); +/// Decode a raw Intel PT trace for a single thread given in \p buffer and +/// append the decoded instructions and errors in \p decoded_thread. It uses the +/// low level libipt library underneath. +/// +/// \return +/// An \a llvm::Error if the decoder couldn't be properly set up. +llvm::Error DecodeSingleTraceForThread(DecodedThread &decoded_thread, + TraceIntelPT &trace_intel_pt, + llvm::ArrayRef buffer); /// Decode a raw Intel PT trace for a single thread that was collected in a per /// cpu core basis. @@ -66,7 +70,11 @@ void DecodeSingleTraceForThread(DecodedThread &decoded_thread, TraceIntelPT &tra /// A list of chunks of timed executions of the same given thread. It is used /// to identify if some executions have missing intel pt data and also to /// determine in which core a certain part of the execution ocurred. -void DecodeSystemWideTraceForThread( +/// +/// \return +/// An \a llvm::Error if the decoder couldn't be properly set up, i.e. no +/// instructions were attempted to be decoded. +llvm::Error DecodeSystemWideTraceForThread( DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt, const llvm::DenseMap> &buffers, const std::vector &executions); diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp index c9333bd..8b90afb 100644 --- a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.cpp @@ -23,27 +23,33 @@ using namespace llvm; ThreadDecoder::ThreadDecoder(const ThreadSP &thread_sp, TraceIntelPT &trace) : m_thread_sp(thread_sp), m_trace(trace) {} -DecodedThreadSP ThreadDecoder::Decode() { - if (!m_decoded_thread.hasValue()) - m_decoded_thread = DoDecode(); +Expected ThreadDecoder::Decode() { + if (!m_decoded_thread.hasValue()) { + if (Expected decoded_thread = DoDecode()) { + m_decoded_thread = *decoded_thread; + } else { + return decoded_thread.takeError(); + } + } return *m_decoded_thread; } -DecodedThreadSP ThreadDecoder::DoDecode() { +llvm::Expected ThreadDecoder::DoDecode() { return m_trace.GetTimer() .ForThread(m_thread_sp->GetID()) - .TimeTask("Decoding instructions", [&]() { - DecodedThreadSP decoded_thread_sp = - std::make_shared(m_thread_sp); - - Error err = m_trace.OnThreadBufferRead( - m_thread_sp->GetID(), [&](llvm::ArrayRef data) { - DecodeSingleTraceForThread(*decoded_thread_sp, m_trace, data); - return Error::success(); - }); - - if (err) - decoded_thread_sp->SetAsFailed(std::move(err)); - return decoded_thread_sp; - }); + .TimeTask>( + "Decoding instructions", [&]() -> Expected { + DecodedThreadSP decoded_thread_sp = + std::make_shared(m_thread_sp); + + Error err = m_trace.OnThreadBufferRead( + m_thread_sp->GetID(), [&](llvm::ArrayRef data) { + return DecodeSingleTraceForThread(*decoded_thread_sp, m_trace, + data); + }); + + if (err) + return std::move(err); + return decoded_thread_sp; + }); } diff --git a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h index e5a6931..5c77ad9 100644 --- a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h @@ -34,13 +34,13 @@ public: /// /// \return /// A \a DecodedThread instance. - DecodedThreadSP Decode(); + llvm::Expected Decode(); ThreadDecoder(const ThreadDecoder &other) = delete; ThreadDecoder &operator=(const ThreadDecoder &other) = delete; private: - DecodedThreadSP DoDecode(); + llvm::Expected DoDecode(); lldb::ThreadSP m_thread_sp; TraceIntelPT &m_trace; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp index ebce5d9..da91ba7 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.cpp @@ -23,10 +23,6 @@ TraceCursorIntelPT::TraceCursorIntelPT(ThreadSP thread_sp, Seek(0, SeekType::End); } -int64_t TraceCursorIntelPT::GetItemsCount() const { - return m_decoded_thread_sp->GetInstructionsCount(); -} - void TraceCursorIntelPT::CalculateTscRange() { // If we failed, then we look for the exact range if (!m_tsc_range || !m_tsc_range->InRange(m_pos)) @@ -51,7 +47,7 @@ bool TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) { m_pos = offset; break; case TraceCursor::SeekType::End: - m_pos = GetItemsCount() - 1 + offset; + m_pos = m_decoded_thread_sp->GetItemsCount() - 1 + offset; break; case TraceCursor::SeekType::Current: m_pos += offset; @@ -62,23 +58,23 @@ bool TraceCursorIntelPT::Seek(int64_t offset, SeekType origin) { } bool TraceCursorIntelPT::HasValue() const { - return m_pos >= 0 && m_pos < GetItemsCount(); + return m_pos >= 0 && m_pos < m_decoded_thread_sp->GetItemsCount(); } -bool TraceCursorIntelPT::IsError() { - return m_decoded_thread_sp->IsInstructionAnError(m_pos); +lldb::TraceItemKind TraceCursorIntelPT::GetItemKind() const { + return m_decoded_thread_sp->GetItemKindByIndex(m_pos); } -const char *TraceCursorIntelPT::GetError() { - return m_decoded_thread_sp->GetErrorByInstructionIndex(m_pos); +const char *TraceCursorIntelPT::GetError() const { + return m_decoded_thread_sp->GetErrorByIndex(m_pos); } -lldb::addr_t TraceCursorIntelPT::GetLoadAddress() { +lldb::addr_t TraceCursorIntelPT::GetLoadAddress() const { return m_decoded_thread_sp->GetInstructionLoadAddress(m_pos); } Optional -TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) { +TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) const { switch (counter_type) { case lldb::eTraceCounterTSC: if (m_tsc_range) @@ -88,13 +84,8 @@ TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) { } } -lldb::TraceEvents TraceCursorIntelPT::GetEvents() { - return m_decoded_thread_sp->GetEvents(m_pos); -} - -TraceInstructionControlFlowType -TraceCursorIntelPT::GetInstructionControlFlowType() { - return m_decoded_thread_sp->GetInstructionControlFlowType(m_pos); +lldb::TraceEvent TraceCursorIntelPT::GetEventType() const { + return m_decoded_thread_sp->GetEventByIndex(m_pos); } bool TraceCursorIntelPT::GoToId(user_id_t id) { @@ -107,7 +98,7 @@ bool TraceCursorIntelPT::GoToId(user_id_t id) { } bool TraceCursorIntelPT::HasId(lldb::user_id_t id) const { - return id < m_decoded_thread_sp->GetInstructionsCount(); + return static_cast(id) < m_decoded_thread_sp->GetItemsCount(); } user_id_t TraceCursorIntelPT::GetId() const { return m_pos; } diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h index 37fdb1f..c90431d 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h @@ -25,18 +25,16 @@ public: bool HasValue() const override; - const char *GetError() override; + const char *GetError() const override; - lldb::addr_t GetLoadAddress() override; + lldb::addr_t GetLoadAddress() const override; - llvm::Optional GetCounter(lldb::TraceCounter counter_type) override; + llvm::Optional + GetCounter(lldb::TraceCounter counter_type) const override; - lldb::TraceEvents GetEvents() override; + lldb::TraceEvent GetEventType() const override; - lldb::TraceInstructionControlFlowType - GetInstructionControlFlowType() override; - - bool IsError() override; + lldb::TraceItemKind GetItemKind() const override; bool GoToId(lldb::user_id_t id) override; @@ -45,10 +43,6 @@ public: bool HasId(lldb::user_id_t id) const override; private: - /// \return - /// The number of instructions and errors in the trace. - int64_t GetItemsCount() const; - /// Calculate the tsc range for the current position if needed. void CalculateTscRange(); diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp index ba593f4..fc7a103 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.cpp @@ -124,11 +124,9 @@ TraceIntelPT::TraceIntelPT(JSONTraceBundleDescription &bundle_description, : Trace(traced_processes, bundle_description.GetCpuIds()), m_cpu_info(bundle_description.cpu_info) {} -DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { +Expected TraceIntelPT::Decode(Thread &thread) { if (const char *error = RefreshLiveProcessState()) - return std::make_shared( - thread.shared_from_this(), - createStringError(inconvertibleErrorCode(), error)); + return createStringError(inconvertibleErrorCode(), error); Storage &storage = GetUpdatedStorage(); if (storage.multicpu_decoder) @@ -136,14 +134,16 @@ DecodedThreadSP TraceIntelPT::Decode(Thread &thread) { auto it = storage.thread_decoders.find(thread.GetID()); if (it == storage.thread_decoders.end()) - return std::make_shared( - thread.shared_from_this(), - createStringError(inconvertibleErrorCode(), "thread not traced")); + return createStringError(inconvertibleErrorCode(), "thread not traced"); return it->second->Decode(); } -lldb::TraceCursorUP TraceIntelPT::GetCursor(Thread &thread) { - return Decode(thread)->GetCursor(); +llvm::Expected +TraceIntelPT::CreateNewCursor(Thread &thread) { + if (Expected decoded_thread = Decode(thread)) + return decoded_thread.get()->CreateNewCursor(); + else + return decoded_thread.takeError(); } void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { @@ -157,6 +157,14 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { } s << "\n"; + Expected decoded_thread_sp_or_err = Decode(thread); + if (!decoded_thread_sp_or_err) { + s << toString(decoded_thread_sp_or_err.takeError()) << "\n"; + return; + } + + DecodedThreadSP &decoded_thread_sp = *decoded_thread_sp_or_err; + Expected> raw_size_or_error = GetRawTraceSize(thread); if (!raw_size_or_error) { s.Format(" {0}\n", toString(raw_size_or_error.takeError())); @@ -164,14 +172,12 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { } Optional raw_size = *raw_size_or_error; - DecodedThreadSP decoded_trace_sp = Decode(thread); - /// Instruction stats { - uint64_t insn_len = decoded_trace_sp->GetInstructionsCount(); - uint64_t mem_used = decoded_trace_sp->CalculateApproximateMemoryUsage(); + uint64_t items_count = decoded_thread_sp->GetItemsCount(); + uint64_t mem_used = decoded_thread_sp->CalculateApproximateMemoryUsage(); - s.Format(" Total number of instructions: {0}\n", insn_len); + s.Format(" Total number of trace items: {0}\n", items_count); s << "\n Memory usage:\n"; if (raw_size) @@ -180,11 +186,10 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { s.Format( " Total approximate memory usage (excluding raw trace): {0:2} KiB\n", (double)mem_used / 1024); - if (insn_len != 0) - s.Format( - " Average memory usage per instruction (excluding raw trace): " - "{0:2} bytes\n", - (double)mem_used / insn_len); + if (items_count != 0) + s.Format(" Average memory usage per item (excluding raw trace): " + "{0:2} bytes\n", + (double)mem_used / items_count); } // Timing @@ -203,15 +208,13 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { // Instruction events stats { const DecodedThread::EventsStats &events_stats = - decoded_trace_sp->GetEventsStats(); + decoded_thread_sp->GetEventsStats(); s << "\n Events:\n"; - s.Format(" Number of instructions with events: {0}\n", - events_stats.total_instructions_with_events); s.Format(" Number of individual events: {0}\n", events_stats.total_count); for (const auto &event_to_count : events_stats.events_counts) { s.Format(" {0}: {1}\n", - trace_event_utils::EventToDisplayString(event_to_count.first), + TraceCursor::EventKindToString(event_to_count.first), event_to_count.second); } } @@ -229,7 +232,7 @@ void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) { { s << "\n Errors:\n"; const DecodedThread::LibiptErrorsStats &tsc_errors_stats = - decoded_trace_sp->GetTscErrorsStats(); + decoded_thread_sp->GetTscErrorsStats(); s.Format(" Number of TSC decoding errors: {0}\n", tsc_errors_stats.total_count); for (const auto &error_message_to_count : diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h index 32bbddc7..09ecbe7 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -70,7 +70,7 @@ public: llvm::StringRef GetSchema() override; - lldb::TraceCursorUP GetCursor(Thread &thread) override; + llvm::Expected CreateNewCursor(Thread &thread) override; void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) override; @@ -202,8 +202,9 @@ private: /// /// \return /// A \a DecodedThread shared pointer with the decoded instructions. Any - /// errors are embedded in the instruction list. - DecodedThreadSP Decode(Thread &thread); + /// errors are embedded in the instruction list. An \a llvm::Error is + /// returned if the decoder couldn't be properly set up. + llvm::Expected Decode(Thread &thread); /// We package all the data that can change upon process stops to make sure /// this contract is very visible. diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp index e59b701..d2dbc04 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.cpp @@ -35,10 +35,10 @@ bool TraceIntelPTMultiCpuDecoder::TracesThread(lldb::tid_t tid) const { return m_tids.count(tid); } -DecodedThreadSP TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) { +Expected TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) { if (Error err = CorrelateContextSwitchesAndIntelPtTraces()) - return std::make_shared(thread.shared_from_this(), - std::move(err)); + return std::move(err); + auto it = m_decoded_threads.find(thread.GetID()); if (it != m_decoded_threads.end()) return it->second; @@ -53,13 +53,13 @@ DecodedThreadSP TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) { [&](const DenseMap> &buffers) -> Error { auto it = m_continuous_executions_per_thread->find(thread.GetID()); if (it != m_continuous_executions_per_thread->end()) - DecodeSystemWideTraceForThread(*decoded_thread_sp, *trace_sp, buffers, - it->second); + return DecodeSystemWideTraceForThread(*decoded_thread_sp, *trace_sp, + buffers, it->second); return Error::success(); }); if (err) - decoded_thread_sp->SetAsFailed(std::move(err)); + return std::move(err); m_decoded_threads.try_emplace(thread.GetID(), decoded_thread_sp); return decoded_thread_sp; diff --git a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h index 26a5e1a..11771e0 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h @@ -36,8 +36,9 @@ public: /// \return /// A \a DecodedThread for the \p thread by decoding its instructions on all - /// CPUs, sorted by TSCs. - DecodedThreadSP Decode(Thread &thread); + /// CPUs, sorted by TSCs. An \a llvm::Error is returned if the decoder + /// couldn't be properly set up. + llvm::Expected Decode(Thread &thread); /// \return /// \b true if the given \p tid is managed by this decoder, regardless of diff --git a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp index 68c2146..9225ba4 100644 --- a/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp +++ b/lldb/source/Plugins/TraceExporter/common/TraceHTR.cpp @@ -146,16 +146,16 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor) return llvm::None; }; - bool more_data_in_trace = true; - while (more_data_in_trace) { - if (cursor.IsError()) { + /* TODO: fix after persona0220's patch on a new way to access instruction + kinds while (cursor.HasValue()) { if (cursor.IsError()) { // Append a load address of 0 for all instructions that an error occured // while decoding. // TODO: Make distinction between errors by storing the error messages. // Currently, all errors are treated the same. m_instruction_layer_up->AppendInstruction(0); cursor.Next(); - more_data_in_trace = cursor.HasValue(); + } else if (cursor.IsEvent()) { + cursor.Next(); } else { lldb::addr_t current_instruction_load_address = cursor.GetLoadAddress(); lldb::TraceInstructionControlFlowType current_instruction_type = @@ -164,7 +164,7 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor) m_instruction_layer_up->AppendInstruction( current_instruction_load_address); cursor.Next(); - more_data_in_trace = cursor.HasValue(); + bool more_data_in_trace = cursor.HasValue(); if (current_instruction_type & lldb::eTraceInstructionControlFlowTypeCall) { if (more_data_in_trace && !cursor.IsError()) { @@ -180,6 +180,7 @@ TraceHTR::TraceHTR(Thread &thread, TraceCursor &cursor) } } } + */ } void HTRBlockMetadata::MergeMetadata( diff --git a/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp index 29b1476..194cc74 100644 --- a/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp +++ b/lldb/source/Plugins/TraceExporter/ctf/CommandObjectThreadTraceExportCTF.cpp @@ -79,9 +79,16 @@ bool CommandObjectThreadTraceExportCTF::DoExecute(Args &command, num_threads); return false; } else { - TraceHTR htr(*thread, *trace_sp->GetCursor(*thread)); - htr.ExecutePasses(); - if (llvm::Error err = htr.Export(m_options.m_file)) { + auto do_work = [&]() -> Error { + Expected cursor = trace_sp->CreateNewCursor(*thread); + if (!cursor) + return cursor.takeError(); + TraceHTR htr(*thread, **cursor); + htr.ExecutePasses(); + return htr.Export(m_options.m_file); + }; + + if (llvm::Error err = do_work()) { result.AppendErrorWithFormat("%s\n", toString(std::move(err)).c_str()); return false; } else { diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt index ef28de7..c75a10c 100644 --- a/lldb/source/Target/CMakeLists.txt +++ b/lldb/source/Target/CMakeLists.txt @@ -72,7 +72,7 @@ add_lldb_library(lldbTarget Trace.cpp TraceCursor.cpp TraceExporter.cpp - TraceInstructionDumper.cpp + TraceDumper.cpp UnixSignals.cpp UnwindAssembly.cpp UnwindLLDB.cpp diff --git a/lldb/source/Target/TraceCursor.cpp b/lldb/source/Target/TraceCursor.cpp index 485cb58..1c3fabc 100644 --- a/lldb/source/Target/TraceCursor.cpp +++ b/lldb/source/Target/TraceCursor.cpp @@ -9,6 +9,7 @@ #include "lldb/Target/TraceCursor.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Trace.h" using namespace lldb; using namespace lldb_private; @@ -25,20 +26,27 @@ void TraceCursor::SetForwards(bool forwards) { m_forwards = forwards; } bool TraceCursor::IsForwards() const { return m_forwards; } -const char *trace_event_utils::EventToDisplayString(lldb::TraceEvents event) { - switch (event) { - case lldb::eTraceEventPaused: - return "paused"; - } - return nullptr; +bool TraceCursor::IsError() const { + return GetItemKind() == lldb::eTraceItemKindError; +} + +bool TraceCursor::IsEvent() const { + return GetItemKind() == lldb::eTraceItemKindEvent; +} + +bool TraceCursor::IsInstruction() const { + return GetItemKind() == lldb::eTraceItemKindInstruction; +} + +const char *TraceCursor::GetEventTypeAsString() const { + return EventKindToString(GetEventType()); } -void trace_event_utils::ForEachEvent( - lldb::TraceEvents events, - std::function callback) { - while (events) { - TraceEvents event = static_cast(events & ~(events - 1)); - callback(event); - events &= ~event; +const char *TraceCursor::EventKindToString(lldb::TraceEvent event_kind) { + switch (event_kind) { + case lldb::eTraceEventDisabledHW: + return "hardware disabled tracing"; + case lldb::eTraceEventDisabledSW: + return "software disabled tracing"; } } diff --git a/lldb/source/Target/TraceInstructionDumper.cpp b/lldb/source/Target/TraceDumper.cpp similarity index 59% rename from lldb/source/Target/TraceInstructionDumper.cpp rename to lldb/source/Target/TraceDumper.cpp index 9e71686..6a5fd02 100644 --- a/lldb/source/Target/TraceInstructionDumper.cpp +++ b/lldb/source/Target/TraceDumper.cpp @@ -1,4 +1,4 @@ -//===-- TraceInstructionDumper.cpp ----------------------------------------===// +//===-- TraceDumper.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// -#include "lldb/Target/TraceInstructionDumper.h" +#include "lldb/Target/TraceDumper.h" #include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" @@ -29,11 +29,10 @@ static Optional ToOptionalString(const char *s) { /// \return /// The module name (basename if the module is a file, or the actual name if /// it's a virtual module), or \b nullptr if no name nor module was found. -static const char * -GetModuleName(const TraceInstructionDumper::InstructionEntry &insn) { - if (!insn.symbol_info || !insn.symbol_info->sc.module_sp) +static const char *GetModuleName(const TraceDumper::TraceItem &item) { + if (!item.symbol_info || !item.symbol_info->sc.module_sp) return nullptr; - return insn.symbol_info->sc.module_sp->GetFileSpec() + return item.symbol_info->sc.module_sp->GetFileSpec() .GetFilename() .AsCString(); } @@ -66,9 +65,9 @@ static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) { /// - symbol /// - function /// - line -static bool IsSameInstructionSymbolContext( - const TraceInstructionDumper::SymbolInfo &prev_insn, - const TraceInstructionDumper::SymbolInfo &insn) { +static bool +IsSameInstructionSymbolContext(const TraceDumper::SymbolInfo &prev_insn, + const TraceDumper::SymbolInfo &insn) { // module checks if (insn.sc.module_sp != prev_insn.sc.module_sp) return false; @@ -92,31 +91,30 @@ static bool IsSameInstructionSymbolContext( return curr_line_valid == prev_line_valid; } -class OutputWriterCLI : public TraceInstructionDumper::OutputWriter { +class OutputWriterCLI : public TraceDumper::OutputWriter { public: - OutputWriterCLI(Stream &s, const TraceInstructionDumperOptions &options) - : m_s(s), m_options(options){}; - - void InfoMessage(StringRef text) override { m_s << " " << text << "\n"; } + OutputWriterCLI(Stream &s, const TraceDumperOptions &options, Thread &thread) + : m_s(s), m_options(options) { + m_s.Format("thread #{0}: tid = {1}\n", thread.GetIndexID(), thread.GetID()); + }; - void Event(StringRef text) override { m_s.Format(" [{0}]\n", text); } + void NoMoreData() override { m_s << " no more data\n"; } - void - Instruction(const TraceInstructionDumper::InstructionEntry &insn) override { - if (insn.symbol_info) { - if (!insn.prev_symbol_info || - !IsSameInstructionSymbolContext(*insn.prev_symbol_info, - *insn.symbol_info)) { + void TraceItem(const TraceDumper::TraceItem &item) override { + if (item.symbol_info) { + if (!item.prev_symbol_info || + !IsSameInstructionSymbolContext(*item.prev_symbol_info, + *item.symbol_info)) { m_s << " "; - const char *module_name = GetModuleName(insn); + const char *module_name = GetModuleName(item); if (!module_name) m_s << "(none)"; - else if (!insn.symbol_info->sc.function && !insn.symbol_info->sc.symbol) + else if (!item.symbol_info->sc.function && !item.symbol_info->sc.symbol) m_s.Format("{0}`(none)", module_name); else - insn.symbol_info->sc.DumpStopContext( - &m_s, insn.symbol_info->exe_ctx.GetTargetPtr(), - insn.symbol_info->address, + item.symbol_info->sc.DumpStopContext( + &m_s, item.symbol_info->exe_ctx.GetTargetPtr(), + item.symbol_info->address, /*show_fullpaths=*/false, /*show_module=*/true, /*show_inlined_frames=*/false, /*show_function_arguments=*/true, @@ -125,53 +123,57 @@ public: } } - if (insn.error && !m_was_prev_instruction_an_error) - InfoMessage("...missing instructions"); + if (item.error && !m_was_prev_instruction_an_error) + m_s << " ...missing instructions\n"; - m_s.Format(" {0}: ", insn.id); + m_s.Format(" {0}: ", item.id); if (m_options.show_tsc) { m_s << "[tsc="; - if (insn.tsc) - m_s.Format("{0}", *insn.tsc); + if (item.tsc) + m_s.Format("{0}", *item.tsc); else m_s << "unavailable"; m_s << "] "; } - if (insn.error) { - m_s << *insn.error; - m_was_prev_instruction_an_error = true; + if (item.event) { + m_s << "(event) " << TraceCursor::EventKindToString(*item.event); + } else if (item.error) { + m_s << "(error) " << *item.error; } else { - m_s.Format("{0:x+16}", insn.load_address); - if (insn.symbol_info) { + m_s.Format("{0:x+16}", item.load_address); + if (item.symbol_info) { m_s << " "; - insn.symbol_info->instruction->Dump(&m_s, /*max_opcode_byte_size=*/0, + item.symbol_info->instruction->Dump(&m_s, /*max_opcode_byte_size=*/0, /*show_address=*/false, /*show_bytes=*/false, - &insn.symbol_info->exe_ctx, - &insn.symbol_info->sc, + &item.symbol_info->exe_ctx, + &item.symbol_info->sc, /*prev_sym_ctx=*/nullptr, /*disassembly_addr_format=*/nullptr, /*max_address_text_size=*/0); } - m_was_prev_instruction_an_error = false; } + + m_was_prev_instruction_an_error = (bool)item.error; m_s << "\n"; } private: Stream &m_s; - TraceInstructionDumperOptions m_options; + TraceDumperOptions m_options; bool m_was_prev_instruction_an_error = false; }; -class OutputWriterJSON : public TraceInstructionDumper::OutputWriter { +class OutputWriterJSON : public TraceDumper::OutputWriter { /* schema: error_message: string | { + "id": decimal, + "tsc"?: string decimal, "event": string } | { "id": decimal, @@ -189,7 +191,7 @@ class OutputWriterJSON : public TraceInstructionDumper::OutputWriter { } */ public: - OutputWriterJSON(Stream &s, const TraceInstructionDumperOptions &options) + OutputWriterJSON(Stream &s, const TraceDumperOptions &options) : m_s(s), m_options(options), m_j(m_s.AsRawOstream(), /*IndentSize=*/options.pretty_print_json ? 2 : 0) { @@ -198,42 +200,45 @@ public: ~OutputWriterJSON() { m_j.arrayEnd(); } - void Event(StringRef text) override { - m_j.object([&] { m_j.attribute("event", text); }); - } - - void - Instruction(const TraceInstructionDumper::InstructionEntry &insn) override { + void TraceItem(const TraceDumper::TraceItem &item) override { m_j.object([&] { - m_j.attribute("id", insn.id); + m_j.attribute("id", item.id); if (m_options.show_tsc) m_j.attribute( "tsc", - insn.tsc ? Optional(std::to_string(*insn.tsc)) : None); + item.tsc ? Optional(std::to_string(*item.tsc)) : None); - if (insn.error) { - m_j.attribute("error", *insn.error); + if (item.event) { + m_j.object([&] { + m_j.attribute("event", TraceCursor::EventKindToString(*item.event)); + }); return; } - m_j.attribute("loadAddress", formatv("{0:x}", insn.load_address)); - if (insn.symbol_info) { - m_j.attribute("module", ToOptionalString(GetModuleName(insn))); + if (item.error) { + m_j.attribute("error", *item.error); + return; + } + + // we know we are seeing an actual instruction + m_j.attribute("loadAddress", formatv("{0:x}", item.load_address)); + if (item.symbol_info) { + m_j.attribute("module", ToOptionalString(GetModuleName(item))); m_j.attribute("symbol", ToOptionalString( - insn.symbol_info->sc.GetFunctionName().AsCString())); + item.symbol_info->sc.GetFunctionName().AsCString())); m_j.attribute( "mnemonic", - ToOptionalString(insn.symbol_info->instruction->GetMnemonic( - &insn.symbol_info->exe_ctx))); + ToOptionalString(item.symbol_info->instruction->GetMnemonic( + &item.symbol_info->exe_ctx))); - if (IsLineEntryValid(insn.symbol_info->sc.line_entry)) { + if (IsLineEntryValid(item.symbol_info->sc.line_entry)) { m_j.attribute( "source", ToOptionalString( - insn.symbol_info->sc.line_entry.file.GetPath().c_str())); - m_j.attribute("line", insn.symbol_info->sc.line_entry.line); - m_j.attribute("column", insn.symbol_info->sc.line_entry.column); + item.symbol_info->sc.line_entry.file.GetPath().c_str())); + m_j.attribute("line", item.symbol_info->sc.line_entry.line); + m_j.attribute("column", item.symbol_info->sc.line_entry.column); } } }); @@ -241,25 +246,25 @@ public: private: Stream &m_s; - TraceInstructionDumperOptions m_options; + TraceDumperOptions m_options; json::OStream m_j; }; -static std::unique_ptr -CreateWriter(Stream &s, const TraceInstructionDumperOptions &options) { +static std::unique_ptr +CreateWriter(Stream &s, const TraceDumperOptions &options, Thread &thread) { if (options.json) - return std::unique_ptr( + return std::unique_ptr( new OutputWriterJSON(s, options)); else - return std::unique_ptr( - new OutputWriterCLI(s, options)); + return std::unique_ptr( + new OutputWriterCLI(s, options, thread)); } -TraceInstructionDumper::TraceInstructionDumper( - lldb::TraceCursorUP &&cursor_up, Stream &s, - const TraceInstructionDumperOptions &options) +TraceDumper::TraceDumper(lldb::TraceCursorUP &&cursor_up, Stream &s, + const TraceDumperOptions &options) : m_cursor_up(std::move(cursor_up)), m_options(options), - m_writer_up(CreateWriter(s, m_options)) { + m_writer_up(CreateWriter( + s, m_options, *m_cursor_up->GetExecutionContextRef().GetThreadSP())) { if (m_options.id) m_cursor_up->GoToId(*m_options.id); @@ -275,31 +280,20 @@ TraceInstructionDumper::TraceInstructionDumper( } } -TraceInstructionDumper::InstructionEntry -TraceInstructionDumper::CreatRawInstructionEntry() { - InstructionEntry insn; - insn.id = m_cursor_up->GetId(); +TraceDumper::TraceItem TraceDumper::CreatRawTraceItem() { + TraceItem item; + item.id = m_cursor_up->GetId(); if (m_options.show_tsc) - insn.tsc = m_cursor_up->GetCounter(lldb::eTraceCounterTSC); - return insn; -} - -void TraceInstructionDumper::PrintEvents() { - if (!m_options.show_events) - return; - - trace_event_utils::ForEachEvent( - m_cursor_up->GetEvents(), [&](TraceEvents event) { - m_writer_up->Event(trace_event_utils::EventToDisplayString(event)); - }); + item.tsc = m_cursor_up->GetCounter(lldb::eTraceCounterTSC); + return item; } /// Find the symbol context for the given address reusing the previous /// instruction's symbol context when possible. -static SymbolContext CalculateSymbolContext( - const Address &address, - const TraceInstructionDumper::SymbolInfo &prev_symbol_info) { +static SymbolContext +CalculateSymbolContext(const Address &address, + const TraceDumper::SymbolInfo &prev_symbol_info) { AddressRange range; if (prev_symbol_info.sc.GetAddressRange(eSymbolContextEverything, 0, /*inline_block_range*/ false, @@ -315,8 +309,8 @@ static SymbolContext CalculateSymbolContext( /// Find the disassembler for the given address reusing the previous /// instruction's disassembler when possible. static std::tuple -CalculateDisass(const TraceInstructionDumper::SymbolInfo &symbol_info, - const TraceInstructionDumper::SymbolInfo &prev_symbol_info, +CalculateDisass(const TraceDumper::SymbolInfo &symbol_info, + const TraceDumper::SymbolInfo &prev_symbol_info, const ExecutionContext &exe_ctx) { if (prev_symbol_info.disassembler) { if (InstructionSP instruction = @@ -348,62 +342,48 @@ CalculateDisass(const TraceInstructionDumper::SymbolInfo &symbol_info, : InstructionSP()); } -Optional -TraceInstructionDumper::DumpInstructions(size_t count) { +Optional TraceDumper::DumpInstructions(size_t count) { ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP(); - m_writer_up->InfoMessage(formatv("thread #{0}: tid = {1}", - thread_sp->GetIndexID(), thread_sp->GetID()) - .str()); - SymbolInfo prev_symbol_info; Optional last_id; ExecutionContext exe_ctx; thread_sp->GetProcess()->GetTarget().CalculateExecutionContext(exe_ctx); - for (size_t i = 0; i < count && m_cursor_up->HasValue(); - m_cursor_up->Next(), i++) { - last_id = m_cursor_up->GetId(); - - if (m_options.forwards) { - // When moving forwards, we first print the event before printing - // the actual instruction. - PrintEvents(); - } - - InstructionEntry insn = CreatRawInstructionEntry(); + for (size_t insn_seen = 0; insn_seen < count && m_cursor_up->HasValue(); + m_cursor_up->Next()) { - if (const char *err = m_cursor_up->GetError()) { - insn.error = err; - m_writer_up->Instruction(insn); + last_id = m_cursor_up->GetId(); + TraceItem item = CreatRawTraceItem(); + + if (m_cursor_up->IsEvent()) { + if (!m_options.show_events) + continue; + item.event = m_cursor_up->GetEventType(); + } else if (m_cursor_up->IsError()) { + item.error = m_cursor_up->GetError(); } else { - insn.load_address = m_cursor_up->GetLoadAddress(); + insn_seen++; + item.load_address = m_cursor_up->GetLoadAddress(); if (!m_options.raw) { SymbolInfo symbol_info; symbol_info.exe_ctx = exe_ctx; - symbol_info.address.SetLoadAddress(insn.load_address, + symbol_info.address.SetLoadAddress(item.load_address, exe_ctx.GetTargetPtr()); symbol_info.sc = CalculateSymbolContext(symbol_info.address, prev_symbol_info); std::tie(symbol_info.disassembler, symbol_info.instruction) = CalculateDisass(symbol_info, prev_symbol_info, exe_ctx); - insn.prev_symbol_info = prev_symbol_info; - insn.symbol_info = symbol_info; + item.prev_symbol_info = prev_symbol_info; + item.symbol_info = symbol_info; prev_symbol_info = symbol_info; } - m_writer_up->Instruction(insn); - } - - if (!m_options.forwards) { - // If we move backwards, we print the events after printing - // the actual instruction so that reading chronologically - // makes sense. - PrintEvents(); } + m_writer_up->TraceItem(item); } if (!m_cursor_up->HasValue()) - m_writer_up->InfoMessage("no more data"); + m_writer_up->NoMoreData(); return last_id; } diff --git a/lldb/test/API/commands/trace/TestTraceDumpInfo.py b/lldb/test/API/commands/trace/TestTraceDumpInfo.py index f54d5e3..76eda96 100644 --- a/lldb/test/API/commands/trace/TestTraceDumpInfo.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInfo.py @@ -37,20 +37,19 @@ class TestTraceDumpInfo(TraceIntelPTTestCaseBase): substrs=['''Trace technology: intel-pt thread #1: tid = 3842849 - Total number of instructions: 21 + Total number of trace items: 23 Memory usage: Raw trace size: 4 KiB - Total approximate memory usage (excluding raw trace): 1.27 KiB - Average memory usage per instruction (excluding raw trace): 61.76 bytes + Total approximate memory usage (excluding raw trace): 0.20 KiB + Average memory usage per item (excluding raw trace): 9.00 bytes Timing for this thread: Decoding instructions: ''', ''' Events: - Number of instructions with events: 1 - Number of individual events: 1 - paused: 1 + Number of individual events: 2 + software disabled tracing: 2 Errors: Number of TSC decoding errors: 0'''], diff --git a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py index 50214d2..d7cb77a 100644 --- a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py @@ -34,7 +34,11 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): substrs=["intel-pt"]) self.expect("thread trace dump instructions --raw --count 5 --forwards --json", - substrs=['''[{"id":0,"loadAddress":"0x400511"},{"id":1,"loadAddress":"0x400518"},{"id":2,"loadAddress":"0x40051f"},{"id":3,"loadAddress":"0x400529"},{"id":4,"loadAddress":"0x40052d"}]''']) + substrs=['''[{"id":0,"loadAddress":"0x400511"}''', + '''{"id":2,"loadAddress":"0x400518"}''', + '''{"id":3,"loadAddress":"0x40051f"}''', + '''{"id":4,"loadAddress":"0x400529"}''', + '''{"id":5,"loadAddress":"0x40052d"}''']) self.expect("thread trace dump instructions --raw --count 5 --forwards --pretty-json", substrs=['''[ @@ -43,19 +47,19 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): "loadAddress": "0x400511" }, { - "id": 1, + "id": 2, "loadAddress": "0x400518" }, { - "id": 2, + "id": 3, "loadAddress": "0x40051f" }, { - "id": 3, + "id": 4, "loadAddress": "0x400529" }, { - "id": 4, + "id": 5, "loadAddress": "0x40052d" } ]''']) @@ -76,19 +80,19 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): "loadAddress": "0x400511" }, { - "id": 1, + "id": 2, "loadAddress": "0x400518" }, { - "id": 2, + "id": 3, "loadAddress": "0x40051f" }, { - "id": 3, + "id": 4, "loadAddress": "0x400529" }, { - "id": 4, + "id": 5, "loadAddress": "0x40052d" } ]''') @@ -101,66 +105,66 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): self.expect("thread trace dump instructions --raw --count 21 --forwards", substrs=['''thread #1: tid = 3842849 0: 0x0000000000400511 - 1: 0x0000000000400518 - 2: 0x000000000040051f - 3: 0x0000000000400529 - 4: 0x000000000040052d - 5: 0x0000000000400521 - 6: 0x0000000000400525 - 7: 0x0000000000400529 - 8: 0x000000000040052d - 9: 0x0000000000400521 - 10: 0x0000000000400525 - 11: 0x0000000000400529 - 12: 0x000000000040052d - 13: 0x0000000000400521 - 14: 0x0000000000400525 - 15: 0x0000000000400529 - 16: 0x000000000040052d - 17: 0x0000000000400521 - 18: 0x0000000000400525 - 19: 0x0000000000400529 - 20: 0x000000000040052d''']) + 2: 0x0000000000400518 + 3: 0x000000000040051f + 4: 0x0000000000400529 + 5: 0x000000000040052d + 6: 0x0000000000400521 + 7: 0x0000000000400525 + 8: 0x0000000000400529 + 9: 0x000000000040052d + 10: 0x0000000000400521 + 11: 0x0000000000400525 + 12: 0x0000000000400529 + 13: 0x000000000040052d + 14: 0x0000000000400521 + 15: 0x0000000000400525 + 16: 0x0000000000400529 + 17: 0x000000000040052d + 18: 0x0000000000400521 + 19: 0x0000000000400525 + 20: 0x0000000000400529 + 21: 0x000000000040052d''']) # We check if we can pass count and skip self.expect("thread trace dump instructions --count 5 --skip 6 --raw --forwards", substrs=['''thread #1: tid = 3842849 - 6: 0x0000000000400525 - 7: 0x0000000000400529 - 8: 0x000000000040052d - 9: 0x0000000000400521 - 10: 0x0000000000400525''']) + 6: 0x0000000000400521 + 7: 0x0000000000400525 + 8: 0x0000000000400529 + 9: 0x000000000040052d + 10: 0x0000000000400521''']) self.expect("thread trace dump instructions --count 5 --skip 6 --raw", substrs=['''thread #1: tid = 3842849 - 14: 0x0000000000400525 - 13: 0x0000000000400521 - 12: 0x000000000040052d - 11: 0x0000000000400529 - 10: 0x0000000000400525''']) + 16: 0x0000000000400529 + 15: 0x0000000000400525 + 14: 0x0000000000400521 + 13: 0x000000000040052d + 12: 0x0000000000400529''']) # We check if we can pass count and skip and instruction id in hex self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 0xA", substrs=['''thread #1: tid = 3842849 - 4: 0x000000000040052d - 3: 0x0000000000400529 - 2: 0x000000000040051f - 1: 0x0000000000400518 - 0: 0x0000000000400511''']) + 4: 0x0000000000400529 + 3: 0x000000000040051f + 2: 0x0000000000400518 + 0: 0x0000000000400511 + no more data''']) # We check if we can pass count and skip and instruction id in decimal self.expect("thread trace dump instructions --count 5 --skip 6 --raw --id 10", substrs=['''thread #1: tid = 3842849 - 4: 0x000000000040052d - 3: 0x0000000000400529 - 2: 0x000000000040051f - 1: 0x0000000000400518 - 0: 0x0000000000400511''']) + 4: 0x0000000000400529 + 3: 0x000000000040051f + 2: 0x0000000000400518 + 0: 0x0000000000400511 + no more data''']) # We check if we can access the thread by index id self.expect("thread trace dump instructions 1 --raw", substrs=['''thread #1: tid = 3842849 - 20: 0x000000000040052d''']) + 21: 0x000000000040052d''']) # We check that we get an error when using an invalid thread index id self.expect("thread trace dump instructions 10", error=True, @@ -175,31 +179,30 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): self.expect("thread trace dump instructions 2 --count 2", substrs=['''thread #2: tid = 3842850 a.out`main + 32 at main.cpp:4 - 20: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 - 19: 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) + 21: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 + 20: 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) # We use custom --count and --skip, saving the command to history for later self.expect("thread trace dump instructions 2 --count 2 --skip 2", inHistory=True, substrs=['''thread #2: tid = 3842850 - a.out`main + 24 at main.cpp:4 - 18: 0x0000000000400525 addl $0x1, -0x8(%rbp) - a.out`main + 20 at main.cpp:5 - 17: 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) + a.out`main + 28 at main.cpp:4 + 20: 0x0000000000400529 cmpl $0x3, -0x8(%rbp) + 19: 0x0000000000400525 addl $0x1, -0x8(%rbp)''']) # We use a repeat command twice and ensure the previous count is used and the # start position moves with each command. self.expect("", inHistory=True, substrs=['''thread #2: tid = 3842850 + a.out`main + 20 at main.cpp:5 + 18: 0x0000000000400521 xorl $0x1, -0x4(%rbp) a.out`main + 32 at main.cpp:4 - 16: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5 - 15: 0x0000000000400529 cmpl $0x3, -0x8(%rbp)''']) + 17: 0x000000000040052d jle 0x400521 ; <+20> at main.cpp:5''']) self.expect("", inHistory=True, substrs=['''thread #2: tid = 3842850 - a.out`main + 24 at main.cpp:4 - 14: 0x0000000000400525 addl $0x1, -0x8(%rbp) - a.out`main + 20 at main.cpp:5 - 13: 0x0000000000400521 xorl $0x1, -0x4(%rbp)''']) + a.out`main + 28 at main.cpp:4 + 16: 0x0000000000400529 cmpl $0x3, -0x8(%rbp) + 15: 0x0000000000400525 addl $0x1, -0x8(%rbp''']) def testInvalidBounds(self): self.expect("trace load -v " + @@ -211,8 +214,8 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): a.out`main + 4 at main.cpp:2 0: 0x0000000000400511 movl $0x0, -0x4(%rbp) a.out`main + 11 at main.cpp:4 - 1: 0x0000000000400518 movl $0x0, -0x8(%rbp) - 2: 0x000000000040051f jmp 0x400529 ; <+28> at main.cpp:4''']) + 2: 0x0000000000400518 movl $0x0, -0x8(%rbp) + 3: 0x000000000040051f jmp 0x400529 ; <+28> at main.cpp:4''']) # Should print no instructions if the position is out of bounds self.expect("thread trace dump instructions --skip 23", @@ -228,40 +231,55 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): self.expect("thread trace dump instructions --forwards", substrs=['''thread #1: tid = 3842849 ...missing instructions - 0: 0x0000000000400511 error: no memory mapped at this address - 1: 0x0000000000400518 error: no memory mapped at this address''']) + 0: (error) no memory mapped at this address: 0x0000000000400511 + 1: (error) no memory mapped at this address: 0x0000000000400518''']) def testWrongCPU(self): self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace", "trace_wrong_cpu.json")) self.expect("thread trace dump instructions --forwards", - substrs=['''thread #1: tid = 3842849 - ...missing instructions - 0: error: unknown cpu''']) + substrs=["error: unknown cpu"], error=True) def testMultiFileTraceWithMissingModuleInJSON(self): self.expect("trace load " + os.path.join(self.getSourceDir(), "intelpt-trace-multi-file", "multi-file-no-ld.json")) - self.expect("thread trace dump instructions --count 3 --id 4 --forwards --pretty-json", + self.expect("thread trace dump instructions --count 4 --id 4 --forwards --pretty-json", substrs=['''[ { "id": 4, + "loadAddress": "0x40054b", + "module": "a.out", + "symbol": "foo()", + "mnemonic": "jmp" + }, + { + "id": 5, "loadAddress": "0x400510", "module": "a.out", "symbol": null, "mnemonic": "pushq" }, { - "id": 5, + "id": 6, "loadAddress": "0x400516", "module": "a.out", "symbol": null, "mnemonic": "jmpq" }, { - "id": 6, - "error": "0x00007ffff7df1950 error: no memory mapped at this address" + "id": 7, + "error": "no memory mapped at this address: 0x00007ffff7df1950" + }, + { + "id": 8, + "loadAddress": "0x400674", + "module": "a.out", + "symbol": "main", + "mnemonic": "movl", + "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp", + "line": 10, + "column": 0 } ]''']) @@ -269,40 +287,40 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): substrs=['''[ { "id": 20, - "loadAddress": "0x400540", + "loadAddress": "0x40069a", "module": "a.out", - "symbol": "foo()", - "mnemonic": "jmpq" + "symbol": "main", + "mnemonic": "addl", + "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp", + "line": 14, + "column": 0 }, { "id": 21, - "loadAddress": "0x7ffff7bd96e0", - "module": "libfoo.so", - "symbol": "foo()", - "mnemonic": "pushq", - "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", - "line": 3, + "loadAddress": "0x40069c", + "module": "a.out", + "symbol": "main", + "mnemonic": "movl", + "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp", + "line": 14, "column": 0 }, { - "id": 22, - "loadAddress": "0x7ffff7bd96e1", - "module": "libfoo.so", - "symbol": "foo()", - "mnemonic": "movq", - "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", - "line": 3, + "id": 23, + "loadAddress": "0x40069f", + "module": "a.out", + "symbol": "main", + "mnemonic": "callq", + "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/main.cpp", + "line": 16, "column": 0 }, { - "id": 23, - "loadAddress": "0x7ffff7bd96e4", - "module": "libfoo.so", + "id": 24, + "loadAddress": "0x400540", + "module": "a.out", "symbol": "foo()", - "mnemonic": "subq", - "source": "/home/wallace/llvm-sand/external/llvm-project/lldb/test/API/commands/trace/intelpt-trace-multi-file/foo.cpp", - "line": 4, - "column": 0 + "mnemonic": "jmpq" } ]''']) @@ -329,145 +347,147 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): a.out`main + 15 at main.cpp:10 0: 0x000000000040066f callq 0x400540 ; symbol stub for: foo() a.out`symbol stub for: foo() - 1: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 - 2: 0x0000000000400546 pushq $0x2 - 3: 0x000000000040054b jmp 0x400510 + 2: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 + 3: 0x0000000000400546 pushq $0x2 + 4: 0x000000000040054b jmp 0x400510 a.out`(none) - 4: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 - 5: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 + 5: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 + 6: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 ...missing instructions - 6: 0x00007ffff7df1950 error: no memory mapped at this address + 7: (error) no memory mapped at this address: 0x00007ffff7df1950 a.out`main + 20 at main.cpp:10 - 7: 0x0000000000400674 movl %eax, -0xc(%rbp) + 8: 0x0000000000400674 movl %eax, -0xc(%rbp) a.out`main + 23 at main.cpp:12 - 8: 0x0000000000400677 movl -0xc(%rbp), %eax - 9: 0x000000000040067a addl $0x1, %eax - 10: 0x000000000040067f movl %eax, -0xc(%rbp) + 10: 0x0000000000400677 movl -0xc(%rbp), %eax + 11: 0x000000000040067a addl $0x1, %eax + 12: 0x000000000040067f movl %eax, -0xc(%rbp) a.out`main + 34 [inlined] inline_function() at main.cpp:4 - 11: 0x0000000000400682 movl $0x0, -0x4(%rbp) + 14: 0x0000000000400682 movl $0x0, -0x4(%rbp) a.out`main + 41 [inlined] inline_function() + 7 at main.cpp:5 - 12: 0x0000000000400689 movl -0x4(%rbp), %eax - 13: 0x000000000040068c addl $0x1, %eax - 14: 0x0000000000400691 movl %eax, -0x4(%rbp) + 15: 0x0000000000400689 movl -0x4(%rbp), %eax + 16: 0x000000000040068c addl $0x1, %eax + 17: 0x0000000000400691 movl %eax, -0x4(%rbp) a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 - 15: 0x0000000000400694 movl -0x4(%rbp), %eax + 18: 0x0000000000400694 movl -0x4(%rbp), %eax a.out`main + 55 at main.cpp:14 - 16: 0x0000000000400697 movl -0xc(%rbp), %ecx - 17: 0x000000000040069a addl %eax, %ecx - 18: 0x000000000040069c movl %ecx, -0xc(%rbp) + 19: 0x0000000000400697 movl -0xc(%rbp), %ecx + 20: 0x000000000040069a addl %eax, %ecx + 21: 0x000000000040069c movl %ecx, -0xc(%rbp) a.out`main + 63 at main.cpp:16 - 19: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() + 23: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() a.out`symbol stub for: foo() - 20: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 + 24: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 libfoo.so`foo() at foo.cpp:3 - 21: 0x00007ffff7bd96e0 pushq %rbp - 22: 0x00007ffff7bd96e1 movq %rsp, %rbp + 25: 0x00007ffff7bd96e0 pushq %rbp + 26: 0x00007ffff7bd96e1 movq %rsp, %rbp libfoo.so`foo() + 4 at foo.cpp:4 - 23: 0x00007ffff7bd96e4 subq $0x10, %rsp - 24: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() + 27: 0x00007ffff7bd96e4 subq $0x10, %rsp + 28: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() libfoo.so`symbol stub for: bar() - 25: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 + 29: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 libbar.so`bar() at bar.cpp:1 - 26: 0x00007ffff79d7690 pushq %rbp - 27: 0x00007ffff79d7691 movq %rsp, %rbp + 30: 0x00007ffff79d7690 pushq %rbp + 31: 0x00007ffff79d7691 movq %rsp, %rbp libbar.so`bar() + 4 at bar.cpp:2 - 28: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) + 32: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) libbar.so`bar() + 11 at bar.cpp:3 - 29: 0x00007ffff79d769b movl -0x4(%rbp), %eax - 30: 0x00007ffff79d769e addl $0x1, %eax - 31: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) + 33: 0x00007ffff79d769b movl -0x4(%rbp), %eax + 34: 0x00007ffff79d769e addl $0x1, %eax + 35: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) libbar.so`bar() + 22 at bar.cpp:4 - 32: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax - 33: 0x00007ffff79d76a9 popq %rbp - 34: 0x00007ffff79d76aa retq''', + 36: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax + 37: 0x00007ffff79d76a9 popq %rbp + 38: 0x00007ffff79d76aa retq''', '''libfoo.so`foo() + 13 at foo.cpp:4 - 35: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) + 39: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) libfoo.so`foo() + 16 at foo.cpp:5 - 36: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax - 37: 0x00007ffff7bd96f3 addl $0x1, %eax - 38: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) + 40: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax + 41: 0x00007ffff7bd96f3 addl $0x1, %eax + 42: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) libfoo.so`foo() + 27 at foo.cpp:6 - 39: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax - 40: 0x00007ffff7bd96fe addq $0x10, %rsp - 41: 0x00007ffff7bd9702 popq %rbp - 42: 0x00007ffff7bd9703 retq''', + 43: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax + 44: 0x00007ffff7bd96fe addq $0x10, %rsp + 45: 0x00007ffff7bd9702 popq %rbp + 46: 0x00007ffff7bd9703 retq''', '''a.out`main + 68 at main.cpp:16 - 43: 0x00000000004006a4 movl -0xc(%rbp), %ecx - 44: 0x00000000004006a7 addl %eax, %ecx - 45: 0x00000000004006a9 movl %ecx, -0xc(%rbp)''']) + 47: 0x00000000004006a4 movl -0xc(%rbp), %ecx + 48: 0x00000000004006a7 addl %eax, %ecx + 49: 0x00000000004006a9 movl %ecx, -0xc(%rbp) + no more data''']) self.expect("thread trace dump instructions --count 50", substrs=['''thread #1: tid = 815455 a.out`main + 73 at main.cpp:16 - 45: 0x00000000004006a9 movl %ecx, -0xc(%rbp) - 44: 0x00000000004006a7 addl %eax, %ecx - 43: 0x00000000004006a4 movl -0xc(%rbp), %ecx + 49: 0x00000000004006a9 movl %ecx, -0xc(%rbp) + 48: 0x00000000004006a7 addl %eax, %ecx + 47: 0x00000000004006a4 movl -0xc(%rbp), %ecx libfoo.so`foo() + 35 at foo.cpp:6 - 42: 0x00007ffff7bd9703 retq''', - '''41: 0x00007ffff7bd9702 popq %rbp - 40: 0x00007ffff7bd96fe addq $0x10, %rsp - 39: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax + 46: 0x00007ffff7bd9703 retq''', + '''45: 0x00007ffff7bd9702 popq %rbp + 44: 0x00007ffff7bd96fe addq $0x10, %rsp + 43: 0x00007ffff7bd96fb movl -0x4(%rbp), %eax libfoo.so`foo() + 24 at foo.cpp:5 - 38: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) - 37: 0x00007ffff7bd96f3 addl $0x1, %eax - 36: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax + 42: 0x00007ffff7bd96f8 movl %eax, -0x4(%rbp) + 41: 0x00007ffff7bd96f3 addl $0x1, %eax + 40: 0x00007ffff7bd96f0 movl -0x4(%rbp), %eax libfoo.so`foo() + 13 at foo.cpp:4 - 35: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) + 39: 0x00007ffff7bd96ed movl %eax, -0x4(%rbp) libbar.so`bar() + 26 at bar.cpp:4 - 34: 0x00007ffff79d76aa retq''', - '''33: 0x00007ffff79d76a9 popq %rbp - 32: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax + 38: 0x00007ffff79d76aa retq''', + '''37: 0x00007ffff79d76a9 popq %rbp + 36: 0x00007ffff79d76a6 movl -0x4(%rbp), %eax libbar.so`bar() + 19 at bar.cpp:3 - 31: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) - 30: 0x00007ffff79d769e addl $0x1, %eax - 29: 0x00007ffff79d769b movl -0x4(%rbp), %eax + 35: 0x00007ffff79d76a3 movl %eax, -0x4(%rbp) + 34: 0x00007ffff79d769e addl $0x1, %eax + 33: 0x00007ffff79d769b movl -0x4(%rbp), %eax libbar.so`bar() + 4 at bar.cpp:2 - 28: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) + 32: 0x00007ffff79d7694 movl $0x1, -0x4(%rbp) libbar.so`bar() + 1 at bar.cpp:1 - 27: 0x00007ffff79d7691 movq %rsp, %rbp - 26: 0x00007ffff79d7690 pushq %rbp + 31: 0x00007ffff79d7691 movq %rsp, %rbp + 30: 0x00007ffff79d7690 pushq %rbp libfoo.so`symbol stub for: bar() - 25: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 + 29: 0x00007ffff7bd95d0 jmpq *0x200a4a(%rip) ; _GLOBAL_OFFSET_TABLE_ + 32 libfoo.so`foo() + 8 at foo.cpp:4 - 24: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() - 23: 0x00007ffff7bd96e4 subq $0x10, %rsp + 28: 0x00007ffff7bd96e8 callq 0x7ffff7bd95d0 ; symbol stub for: bar() + 27: 0x00007ffff7bd96e4 subq $0x10, %rsp libfoo.so`foo() + 1 at foo.cpp:3 - 22: 0x00007ffff7bd96e1 movq %rsp, %rbp - 21: 0x00007ffff7bd96e0 pushq %rbp + 26: 0x00007ffff7bd96e1 movq %rsp, %rbp + 25: 0x00007ffff7bd96e0 pushq %rbp a.out`symbol stub for: foo() - 20: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 + 24: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 a.out`main + 63 at main.cpp:16 - 19: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() + 23: 0x000000000040069f callq 0x400540 ; symbol stub for: foo() a.out`main + 60 at main.cpp:14 - 18: 0x000000000040069c movl %ecx, -0xc(%rbp) - 17: 0x000000000040069a addl %eax, %ecx - 16: 0x0000000000400697 movl -0xc(%rbp), %ecx + 21: 0x000000000040069c movl %ecx, -0xc(%rbp) + 20: 0x000000000040069a addl %eax, %ecx + 19: 0x0000000000400697 movl -0xc(%rbp), %ecx a.out`main + 52 [inlined] inline_function() + 18 at main.cpp:6 - 15: 0x0000000000400694 movl -0x4(%rbp), %eax + 18: 0x0000000000400694 movl -0x4(%rbp), %eax a.out`main + 49 [inlined] inline_function() + 15 at main.cpp:5 - 14: 0x0000000000400691 movl %eax, -0x4(%rbp) - 13: 0x000000000040068c addl $0x1, %eax - 12: 0x0000000000400689 movl -0x4(%rbp), %eax + 17: 0x0000000000400691 movl %eax, -0x4(%rbp) + 16: 0x000000000040068c addl $0x1, %eax + 15: 0x0000000000400689 movl -0x4(%rbp), %eax a.out`main + 34 [inlined] inline_function() at main.cpp:4 - 11: 0x0000000000400682 movl $0x0, -0x4(%rbp) + 14: 0x0000000000400682 movl $0x0, -0x4(%rbp) a.out`main + 31 at main.cpp:12 - 10: 0x000000000040067f movl %eax, -0xc(%rbp) - 9: 0x000000000040067a addl $0x1, %eax - 8: 0x0000000000400677 movl -0xc(%rbp), %eax + 12: 0x000000000040067f movl %eax, -0xc(%rbp) + 11: 0x000000000040067a addl $0x1, %eax + 10: 0x0000000000400677 movl -0xc(%rbp), %eax a.out`main + 20 at main.cpp:10 - 7: 0x0000000000400674 movl %eax, -0xc(%rbp) + 8: 0x0000000000400674 movl %eax, -0xc(%rbp) ...missing instructions - 6: 0x00007ffff7df1950 error: no memory mapped at this address + 7: (error) no memory mapped at this address: 0x00007ffff7df1950 a.out`(none) - 5: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 - 4: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 + 6: 0x0000000000400516 jmpq *0x200af4(%rip) ; _GLOBAL_OFFSET_TABLE_ + 16 + 5: 0x0000000000400510 pushq 0x200af2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 8 a.out`symbol stub for: foo() + 11 - 3: 0x000000000040054b jmp 0x400510 - 2: 0x0000000000400546 pushq $0x2 - 1: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 + 4: 0x000000000040054b jmp 0x400510 + 3: 0x0000000000400546 pushq $0x2 + 2: 0x0000000000400540 jmpq *0x200ae2(%rip) ; _GLOBAL_OFFSET_TABLE_ + 40 a.out`main + 15 at main.cpp:10 - 0: 0x000000000040066f callq 0x400540 ; symbol stub for: foo()''']) + 0: 0x000000000040066f callq 0x400540 ; symbol stub for: foo() + no more data''']) self.expect("thread trace dump instructions --skip 100 --forwards", inHistory=True, substrs=['''thread #1: tid = 815455 @@ -480,10 +500,12 @@ class TestTraceDumpInstructions(TraceIntelPTTestCaseBase): self.expect("thread trace dump instructions --raw --all --forwards", substrs=['''thread #1: tid = 815455 0: 0x000000000040066f - 1: 0x0000000000400540''', '''5: 0x0000000000400516 + 2: 0x0000000000400540''', + '''6: 0x0000000000400516 ...missing instructions - 6: 0x00007ffff7df1950 error: no memory mapped at this address - 7: 0x0000000000400674''', '''43: 0x00000000004006a4 - 44: 0x00000000004006a7 - 45: 0x00000000004006a9 + 7: (error) no memory mapped at this address: 0x00007ffff7df1950 + 8: 0x0000000000400674''', + '''47: 0x00000000004006a4 + 48: 0x00000000004006a7 + 49: 0x00000000004006a9 no more data''']) diff --git a/lldb/test/API/commands/trace/TestTraceEvents.py b/lldb/test/API/commands/trace/TestTraceEvents.py index 472805f..e72280d 100644 --- a/lldb/test/API/commands/trace/TestTraceEvents.py +++ b/lldb/test/API/commands/trace/TestTraceEvents.py @@ -28,53 +28,53 @@ class TestTraceEvents(TraceIntelPTTestCaseBase): patterns=[f'''thread #1: tid = .* a.out`main \+ 23 at main.cpp:12 0: {ADDRESS_REGEX} movl .* - \[paused\] - 1: {ADDRESS_REGEX} addl .* - 2: {ADDRESS_REGEX} movl .* - \[paused\] - a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4 + 1: \(event\) software disabled tracing + 2: {ADDRESS_REGEX} addl .* 3: {ADDRESS_REGEX} movl .* + 4: \(event\) software disabled tracing + a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4 + 5: {ADDRESS_REGEX} movl .* a.out`main \+ 41 \[inlined\] inline_function\(\) \+ 7 at main.cpp:5 - 4: {ADDRESS_REGEX} movl .* - 5: {ADDRESS_REGEX} addl .* 6: {ADDRESS_REGEX} movl .* + 7: {ADDRESS_REGEX} addl .* + 8: {ADDRESS_REGEX} movl .* a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6 - 7: {ADDRESS_REGEX} movl .* + 9: {ADDRESS_REGEX} movl .* a.out`main \+ 55 at main.cpp:14 - 8: {ADDRESS_REGEX} movl .* - 9: {ADDRESS_REGEX} addl .* 10: {ADDRESS_REGEX} movl .* - \[paused\] + 11: {ADDRESS_REGEX} addl .* + 12: {ADDRESS_REGEX} movl .* + 13: \(event\) software disabled tracing a.out`main \+ 63 at main.cpp:16 - 11: {ADDRESS_REGEX} callq .* ; symbol stub for: foo\(\) - \[paused\] + 14: {ADDRESS_REGEX} callq .* ; symbol stub for: foo\(\) + 15: \(event\) software disabled tracing a.out`symbol stub for: foo\(\) - 12: {ADDRESS_REGEX} jmpq''']) + 16: {ADDRESS_REGEX} jmpq''']) # We ensure that the paused events are printed correctly backward - self.expect("thread trace dump instructions -e --id 12", + self.expect("thread trace dump instructions -e --id 16", patterns=[f'''thread #1: tid = .* a.out`symbol stub for: foo\(\) - 12: {ADDRESS_REGEX} jmpq .* - \[paused\] + 16: {ADDRESS_REGEX} jmpq .* + 15: \(event\) software disabled tracing a.out`main \+ 63 at main.cpp:16 - 11: {ADDRESS_REGEX} callq .* ; symbol stub for: foo\(\) - \[paused\] + 14: {ADDRESS_REGEX} callq .* ; symbol stub for: foo\(\) + 13: \(event\) software disabled tracing a.out`main \+ 60 at main.cpp:14 + 12: {ADDRESS_REGEX} movl .* + 11: {ADDRESS_REGEX} addl .* 10: {ADDRESS_REGEX} movl .* - 9: {ADDRESS_REGEX} addl .* - 8: {ADDRESS_REGEX} movl .* a.out`main \+ 52 \[inlined\] inline_function\(\) \+ 18 at main.cpp:6 - 7: {ADDRESS_REGEX} movl .* + 9: {ADDRESS_REGEX} movl .* a.out`main \+ 49 \[inlined\] inline_function\(\) \+ 15 at main.cpp:5 + 8: {ADDRESS_REGEX} movl .* + 7: {ADDRESS_REGEX} addl .* 6: {ADDRESS_REGEX} movl .* - 5: {ADDRESS_REGEX} addl .* - 4: {ADDRESS_REGEX} movl .* a.out`main \+ 34 \[inlined\] inline_function\(\) at main.cpp:4 - 3: {ADDRESS_REGEX} movl .* - \[paused\] + 5: {ADDRESS_REGEX} movl .* + 4: \(event\) software disabled tracing a.out`main \+ 31 at main.cpp:12 - 2: {ADDRESS_REGEX} movl .* - 1: {ADDRESS_REGEX} addl .* - \[paused\] + 3: {ADDRESS_REGEX} movl .* + 2: {ADDRESS_REGEX} addl .* + 1: \(event\) software disabled tracing 0: {ADDRESS_REGEX} movl .*''']) diff --git a/lldb/test/API/commands/trace/TestTraceExport.py b/lldb/test/API/commands/trace/TestTraceExport.py index 5fdb48e..981df96 100644 --- a/lldb/test/API/commands/trace/TestTraceExport.py +++ b/lldb/test/API/commands/trace/TestTraceExport.py @@ -33,7 +33,7 @@ class TestTraceExport(TraceIntelPTTestCaseBase): error=True) - def testHtrBasicSuperBlockPassFullCheck(self): + def _testHtrBasicSuperBlockPassFullCheck(self): ''' Test the BasicSuperBlock pass of HTR. @@ -116,7 +116,7 @@ class TestTraceExport(TraceIntelPTTestCaseBase): # Check each individual JSON object in "ctf-test.json" against the expected value above self.assertTrue(data[i] == expected[i]) - def testHtrBasicSuperBlockPassSequenceCheck(self): + def _testHtrBasicSuperBlockPassSequenceCheck(self): ''' Test the BasicSuperBlock pass of HTR. diff --git a/lldb/test/API/commands/trace/TestTraceLoad.py b/lldb/test/API/commands/trace/TestTraceLoad.py index 6ba9cea..80cfef3 100644 --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -13,11 +13,11 @@ class TestTraceLoad(TraceIntelPTTestCaseBase): 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=40450075479261144] error: expected tracing enabled event", + substrs=["19522: [tsc=40450075478109270] (error) expected tracing enabled event", "m.out`foo() + 65 at multi_thread.cpp:12:21", "19520: [tsc=40450075477657246] 0x0000000000400ba7 jg 0x400bb3"]) self.expect("thread trace dump instructions 3 -t", - substrs=["67910: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", + substrs=["67911: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", "m.out`bar() + 26 at multi_thread.cpp:20:6"]) @testSBAPIAndCommands @@ -26,11 +26,11 @@ class TestTraceLoad(TraceIntelPTTestCaseBase): 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=40450075479261144] error: expected tracing enabled event", + substrs=["19522: [tsc=40450075478109270] (error) expected tracing enabled event", "m.out`foo() + 65 at multi_thread.cpp:12:21", "19520: [tsc=40450075477657246] 0x0000000000400ba7 jg 0x400bb3"]) self.expect("thread trace dump instructions 3 -t", - substrs=["67910: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", + substrs=["67911: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", "m.out`bar() + 26 at multi_thread.cpp:20:6"]) @testSBAPIAndCommands @@ -39,11 +39,11 @@ class TestTraceLoad(TraceIntelPTTestCaseBase): 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=40450075479261144] error: expected tracing enabled event", + substrs=["19522: [tsc=40450075478109270] (error) expected tracing enabled event", "m.out`foo() + 65 at multi_thread.cpp:12:21", "19520: [tsc=40450075477657246] 0x0000000000400ba7 jg 0x400bb3"]) self.expect("thread trace dump instructions 2 -t", - substrs=["67910: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", + substrs=["67911: [tsc=40450075477799536] 0x0000000000400bd7 addl $0x1, -0x4(%rbp)", "m.out`bar() + 26 at multi_thread.cpp:20:6"]) @testSBAPIAndCommands @@ -74,20 +74,19 @@ class TestTraceLoad(TraceIntelPTTestCaseBase): self.expect("thread trace dump info", substrs=['''Trace technology: intel-pt thread #1: tid = 3842849 - Total number of instructions: 21 + Total number of trace items: 23 Memory usage: Raw trace size: 4 KiB - Total approximate memory usage (excluding raw trace): 1.27 KiB - Average memory usage per instruction (excluding raw trace): 61.76 bytes + Total approximate memory usage (excluding raw trace): 0.20 KiB + Average memory usage per item (excluding raw trace): 9.00 bytes Timing for this thread: Decoding instructions: ''', ''' Events: - Number of instructions with events: 1 - Number of individual events: 1 - paused: 1 + Number of individual events: 2 + software disabled tracing: 2 Errors: Number of TSC decoding errors: 0''']) diff --git a/lldb/test/API/commands/trace/TestTraceStartStop.py b/lldb/test/API/commands/trace/TestTraceStartStop.py index 0f7c924..6808b19 100644 --- a/lldb/test/API/commands/trace/TestTraceStartStop.py +++ b/lldb/test/API/commands/trace/TestTraceStartStop.py @@ -68,7 +68,7 @@ class TestTraceStartStop(TraceIntelPTTestCaseBase): # process stopping should stop the thread self.expect("process trace stop") self.expect("n") - self.expect("thread trace dump instructions", substrs=["not traced"]) + self.expect("thread trace dump instructions", substrs=["not traced"], error=True) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @@ -121,18 +121,18 @@ class TestTraceStartStop(TraceIntelPTTestCaseBase): a.out`main \+ 4 at main.cpp:2 0: {ADDRESS_REGEX} movl .* a.out`main \+ 11 at main.cpp:4 - 1: {ADDRESS_REGEX} movl .* - 2: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 - 3: {ADDRESS_REGEX} cmpl .* - 4: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) + 2: {ADDRESS_REGEX} movl .* + 4: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 + 6: {ADDRESS_REGEX} cmpl .* + 8: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5''']) self.expect("thread trace dump instructions", patterns=[f'''thread #1: tid = .* a.out`main \+ 32 at main.cpp:4 - 4: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5 - 3: {ADDRESS_REGEX} cmpl .* - 2: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 - 1: {ADDRESS_REGEX} movl .* + 8: {ADDRESS_REGEX} jle .* ; <\+20> at main.cpp:5 + 6: {ADDRESS_REGEX} cmpl .* + 4: {ADDRESS_REGEX} jmp .* ; <\+28> at main.cpp:4 + 2: {ADDRESS_REGEX} movl .* a.out`main \+ 4 at main.cpp:2 0: {ADDRESS_REGEX} movl .* ''']) diff --git a/lldb/test/API/commands/trace/TestTraceTSC.py b/lldb/test/API/commands/trace/TestTraceTSC.py index d4aca1c..619deb3 100644 --- a/lldb/test/API/commands/trace/TestTraceTSC.py +++ b/lldb/test/API/commands/trace/TestTraceTSC.py @@ -29,23 +29,23 @@ class TestTraceTimestampCounters(TraceIntelPTTestCaseBase): self.traceStartThread(enableTsc=True) # After each stop there'll be a new TSC - self.expect("n") - self.expect("n") - self.expect("n") + self.expect("si") + self.expect("si") + self.expect("si") # We'll get the most recent instructions, with at least 3 different TSCs - self.runCmd("thread trace dump instructions --tsc --raw") + self.runCmd("thread trace dump instructions --tsc --raw --forward") id_to_tsc = {} for line in self.res.GetOutput().splitlines(): m = re.search(" (.+): \[tsc=(.+)\].*", line) if m: id_to_tsc[int(m.group(1))] = m.group(2) - self.assertEqual(len(id_to_tsc), 6) + self.assertEqual(len(id_to_tsc), 3) # We check that the values are right when dumping a specific id - for id in range(0, 6): + for id, tsc in id_to_tsc.items(): self.expect(f"thread trace dump instructions --tsc --id {id} -c 1", - substrs=[f"{id}: [tsc={id_to_tsc[id]}]"]) + substrs=[f"{id}: [tsc={tsc}"]) @testSBAPIAndCommands @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @@ -77,7 +77,7 @@ class TestTraceTimestampCounters(TraceIntelPTTestCaseBase): patterns=["0: \[tsc=unavailable\] 0x0000000000400511 movl"]) self.expect("thread trace dump instructions --tsc -c 1 --json", - patterns=['''"tsc":null''']) + substrs=['''"tsc":null''']) @testSBAPIAndCommands @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) diff --git a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py index 3276a6e..daa3a30 100644 --- a/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py +++ b/lldb/test/API/commands/trace/multiple-threads/TestTraceStartStopMultipleThreads.py @@ -100,8 +100,8 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase): self.expect("continue") self.expect("thread trace dump instructions", substrs=['main.cpp:4']) self.expect("thread trace dump instructions 3", substrs=['main.cpp:4']) - self.expect("thread trace dump instructions 1", substrs=['not traced']) - self.expect("thread trace dump instructions 2", substrs=['not traced']) + self.expect("thread trace dump instructions 1", substrs=['not traced'], error=True) + self.expect("thread trace dump instructions 2", substrs=['not traced'], error=True) self.traceStopProcess() @@ -133,9 +133,9 @@ class TestTraceStartStopMultipleThreads(TraceIntelPTTestCaseBase): # We'll stop at the next breakpoint in thread 3, and nothing should be traced self.expect("continue") - self.expect("thread trace dump instructions 3", substrs=['not traced']) - self.expect("thread trace dump instructions 1", substrs=['not traced']) - self.expect("thread trace dump instructions 2", substrs=['not traced']) + self.expect("thread trace dump instructions 3", substrs=['not traced'], error=True) + self.expect("thread trace dump instructions 1", substrs=['not traced'], error=True) + self.expect("thread trace dump instructions 2", substrs=['not traced'], error=True) @skipIf(oslist=no_match(['linux']), archs=no_match(['i386', 'x86_64'])) @testSBAPIAndCommands -- 2.7.4