diff --git a/lldb/include/lldb/Target/Trace.h b/lldb/include/lldb/Target/Trace.h index 6e06ceb260e5f..f4d7dee684c35 100644 --- a/lldb/include/lldb/Target/Trace.h +++ b/lldb/include/lldb/Target/Trace.h @@ -167,9 +167,9 @@ class Trace : public PluginInterface, /// /// \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 7640fed93b99f..a4cf6433c19a4 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 @@ class TraceCursor { /// 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 @@ class TraceCursor { /// 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 03aa8d6a6de84..e78836e45b01c 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 @@ class TraceInstructionDumper { 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 @@ class TraceInstructionDumper { 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 @@ class TraceInstructionDumper { /// /// \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 @@ class TraceInstructionDumper { 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 e1e3332eddc58..eba2667727f20 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 7dd89e601af82..993523e067365 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 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed { 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 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed { 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 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed { 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 6e5f1ef4e02e5..7755daa878bee 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 e5d8e2d88490f..578828ff1633f 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 3899c4337e15a..b17e927fafe4b 100644 --- a/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h +++ b/lldb/source/Plugins/Trace/intel-pt/DecodedThread.h @@ -53,6 +53,8 @@ class IntelPTError : public llvm::ErrorInfo { 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 @@ class IntelPTError : public llvm::ErrorInfo { 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 @@ class DecodedThread : public std::enable_shared_from_this { 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 @@ class DecodedThread : public std::enable_shared_from_this { /// 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 @@ class DecodedThread : public std::enable_shared_from_this { 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 @@ class DecodedThread : public std::enable_shared_from_this { 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 @@ class DecodedThread : public std::enable_shared_from_this { 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 c17ef00da2a98..dd34467e38b57 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 @@ class LibiptDecoder { 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 @@ class LibiptDecoder { 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 @@ class LibiptDecoder { 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 @@ class LibiptDecoder { // 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 @@ class LibiptDecoder { /// 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 5420c030b8bd6..cad4d39fcf244 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 c9333bdf16a92..8b90afb219afe 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 e5a6931c9c92c..5c77ad93d27a4 100644 --- a/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/ThreadDecoder.h @@ -34,13 +34,13 @@ class ThreadDecoder { /// /// \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 ebce5d950b15a..da91ba7c13d89 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 37fdb1f5fe8de..c90431de3bbc0 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceCursorIntelPT.h @@ -25,18 +25,16 @@ class TraceCursorIntelPT : public TraceCursor { 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 @@ class TraceCursorIntelPT : public TraceCursor { 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 ba593f473f014..fc7a103fbe150 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 32bbddc7ea8af..09ecbe7da61ae 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPT.h @@ -70,7 +70,7 @@ class TraceIntelPT : public Trace { 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 @@ class TraceIntelPT : public Trace { /// /// \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 e59b7016c0ad3..d2dbc049672c8 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 26a5e1a202c64..11771e018f7bc 100644 --- a/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h +++ b/lldb/source/Plugins/Trace/intel-pt/TraceIntelPTMultiCpuDecoder.h @@ -36,8 +36,9 @@ class TraceIntelPTMultiCpuDecoder { /// \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 68c21462be4f4..9225ba44a1463 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 29b14765fbd6c..194cc74590279 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 ef28de7285fea..c75a10cf61c14 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 485cb58cbb441..1c3fabc4dec03 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 9e71686036c9e..6a5fd0268e025 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 @@ class OutputWriterCLI : public TraceInstructionDumper::OutputWriter { } } - 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 @@ class OutputWriterJSON : public TraceInstructionDumper::OutputWriter { ~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 @@ class OutputWriterJSON : public TraceInstructionDumper::OutputWriter { 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 f54d5e3907bd8..76eda96324fe6 100644 --- a/lldb/test/API/commands/trace/TestTraceDumpInfo.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInfo.py @@ -37,20 +37,19 @@ def testDumpRawTraceSize(self): 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 50214d273e184..d7cb77aafb4a2 100644 --- a/lldb/test/API/commands/trace/TestTraceDumpInstructions.py +++ b/lldb/test/API/commands/trace/TestTraceDumpInstructions.py @@ -34,7 +34,11 @@ def testRawDumpInstructionsInJSON(self): 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 @@ def testRawDumpInstructionsInJSON(self): "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 @@ def testRawDumpInstructionsInJSONToFile(self): "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 @@ def testRawDumpInstructions(self): 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 @@ def testDumpFullInstructionsWithMultipleThreads(self): 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 @@ def testInvalidBounds(self): 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 @@ def testWrongImage(self): 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 @@ def testMultiFileTraceWithMissingModuleInJSON(self): 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 @@ def testMultiFileTraceWithMissingModule(self): 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 @@ def testMultiFileTraceWithMissingModule(self): 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 472805fdfb694..e72280ded686a 100644 --- a/lldb/test/API/commands/trace/TestTraceEvents.py +++ b/lldb/test/API/commands/trace/TestTraceEvents.py @@ -28,53 +28,53 @@ def testPauseEvents(self): 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 5fdb48e7903ea..981df96932fba 100644 --- a/lldb/test/API/commands/trace/TestTraceExport.py +++ b/lldb/test/API/commands/trace/TestTraceExport.py @@ -33,7 +33,7 @@ def testErrorMessages(self): error=True) - def testHtrBasicSuperBlockPassFullCheck(self): + def _testHtrBasicSuperBlockPassFullCheck(self): ''' Test the BasicSuperBlock pass of HTR. @@ -116,7 +116,7 @@ def testHtrBasicSuperBlockPassFullCheck(self): # 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 6ba9cea8ca13a..80cfef3aad2ed 100644 --- a/lldb/test/API/commands/trace/TestTraceLoad.py +++ b/lldb/test/API/commands/trace/TestTraceLoad.py @@ -13,11 +13,11 @@ def testLoadMultiCoreTrace(self): 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 @@ def testLoadMultiCoreTraceWithStringNumbers(self): 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 @@ def testLoadMultiCoreTraceWithMissingThreads(self): 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 @@ def testLoadTrace(self): 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 0f7c924ea8ee9..6808b19ea6935 100644 --- a/lldb/test/API/commands/trace/TestTraceStartStop.py +++ b/lldb/test/API/commands/trace/TestTraceStartStop.py @@ -68,7 +68,7 @@ def testStoppingAThread(self): # 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 @@ def testStartStopLiveThreads(self): 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 d4aca1cf0946c..619deb32d1915 100644 --- a/lldb/test/API/commands/trace/TestTraceTSC.py +++ b/lldb/test/API/commands/trace/TestTraceTSC.py @@ -29,23 +29,23 @@ def testMultipleTscsPerThread(self): 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 @@ def testDumpingAfterTracingWithoutTsc(self): 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 3276a6e3bd9cd..daa3a30c6c22d 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 @@ def testStartMultipleLiveThreadsWithStops(self): 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 @@ def testStartMultipleLiveThreadsWithThreadStartAll(self): # 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