///
/// \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<lldb::TraceCursorUP>
+ CreateNewCursor(Thread &thread) = 0;
/// Dump general info about a given thread's trace. Each Trace plug-in
/// decides which data to show.
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
/// 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
/// 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;
/// }
/// }
/// 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
/// The counter type.
/// \return
/// The value of the counter or \b llvm::None if not available.
- virtual llvm::Optional<uint64_t> 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<uint64_t>
+ 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<void(lldb::TraceEvents event)> callback);
-} // namespace trace_event_utils
-
} // namespace lldb_private
#endif // LLDB_TARGET_TRACE_CURSOR_H
-//===-- 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.
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.
/// 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.
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<uint64_t> tsc;
llvm::Optional<llvm::StringRef> error;
+ llvm::Optional<lldb::TraceEvent> event;
llvm::Optional<SymbolInfo> symbol_info;
llvm::Optional<SymbolInfo> prev_symbol_info;
};
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.
///
/// \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.
llvm::Optional<lldb::user_id_t> 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<OutputWriter> m_writer_up;
};
// 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
#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;
size_t m_count;
size_t m_continue;
llvm::Optional<FileSpec> m_output_file;
- TraceInstructionDumperOptions m_dumper_options;
+ TraceDumperOptions m_dumper_options;
};
CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
m_options.m_dumper_options.id = m_last_id;
}
- TraceCursorUP cursor_up =
- m_exe_ctx.GetTargetSP()->GetTrace()->GetCursor(*thread_sp);
+ llvm::Expected<TraceCursorUP> 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)) {
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;
"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.">;
}
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<int64_t>(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<IntelPTError>(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) {
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::TscRange> 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<lldb::TraceItemKind>(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<TraceCursorIntelPT>(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<size_t, uint64_t>::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<uint64_t>::max()
: next_it->first - 1;
}
return llvm::errc::not_supported;
}
+ int GetLibiptErrorCode() const { return m_libipt_error_code; }
+
void log(llvm::raw_ostream &OS) const override;
private:
lldb::addr_t m_address;
};
-/// Helper struct for building an instruction or error from the decoder.
-/// It holds associated events and timing information.
-struct DecodedInstruction {
- DecodedInstruction() {
- pt_insn.ip = LLDB_INVALID_ADDRESS;
- libipt_error = pte_ok;
- }
-
- DecodedInstruction(int libipt_error_code) : DecodedInstruction() {
- libipt_error = libipt_error_code;
- }
-
- /// \return \b true if and only if this struct holds a libipt error.
- explicit operator bool() const;
-
- int libipt_error;
- lldb::TraceEvents events = (lldb::TraceEvents)0;
- llvm::Optional<uint64_t> 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
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<lldb::TraceEvents, size_t> events_counts;
+ std::unordered_map<lldb::TraceEvent, size_t> 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);
/// 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.
size_t insn_index,
const llvm::Optional<DecodedThread::TscRange> &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.
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<lldb::addr_t> m_instruction_ips;
- /// The size in bytes of each instruction.
- std::vector<uint8_t> m_instruction_sizes;
- /// The libipt instruction class for each instruction.
- std::vector<pt_insn_class> 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<TraceItemStorage> 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<uint8_t> m_item_kinds;
/// This map contains the TSCs of the decoded instructions. It maps
/// `instruction index -> TSC`, where `instruction index` is the first index
std::map<uint64_t, uint64_t> m_instruction_timestamps;
/// This is the chronologically last TSC that has been added.
llvm::Optional<uint64_t> 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<uint64_t, std::string> 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<uint64_t, lldb::TraceEvents> m_events;
/// Statistics of all tracing events.
EventsStats m_events_stats;
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.
///
int status = pt_insn_sync_forward(&m_decoder);
if (IsLibiptError(status)) {
- m_decoded_thread.Append(DecodedInstruction(status));
+ m_decoded_thread.AppendError(IntelPTError(status));
break;
}
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.
///
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);
}
}
// 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;
}
/// 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.
return Error::success();
}
-void lldb_private::trace_intel_pt::DecodeSingleTraceForThread(DecodedThread &decoded_thread,
- TraceIntelPT &trace_intel_pt,
- ArrayRef<uint8_t> buffer) {
+Error lldb_private::trace_intel_pt::DecodeSingleTraceForThread(
+ DecodedThread &decoded_thread, TraceIntelPT &trace_intel_pt,
+ ArrayRef<uint8_t> buffer) {
Expected<PtInsnDecoderUP> 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<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,
const std::vector<IntelPTThreadContinousExecution> &executions) {
Expected<PtInsnDecoderUP> 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));
// 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());
}
}
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<(
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<uint8_t> 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<uint8_t> buffer);
/// Decode a raw Intel PT trace for a single thread that was collected in a per
/// cpu core basis.
/// 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<lldb::cpu_id_t, llvm::ArrayRef<uint8_t>> &buffers,
const std::vector<IntelPTThreadContinousExecution> &executions);
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<DecodedThreadSP> ThreadDecoder::Decode() {
+ if (!m_decoded_thread.hasValue()) {
+ if (Expected<DecodedThreadSP> decoded_thread = DoDecode()) {
+ m_decoded_thread = *decoded_thread;
+ } else {
+ return decoded_thread.takeError();
+ }
+ }
return *m_decoded_thread;
}
-DecodedThreadSP ThreadDecoder::DoDecode() {
+llvm::Expected<DecodedThreadSP> ThreadDecoder::DoDecode() {
return m_trace.GetTimer()
.ForThread(m_thread_sp->GetID())
- .TimeTask<DecodedThreadSP>("Decoding instructions", [&]() {
- DecodedThreadSP decoded_thread_sp =
- std::make_shared<DecodedThread>(m_thread_sp);
-
- Error err = m_trace.OnThreadBufferRead(
- m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> 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<Expected<DecodedThreadSP>>(
+ "Decoding instructions", [&]() -> Expected<DecodedThreadSP> {
+ DecodedThreadSP decoded_thread_sp =
+ std::make_shared<DecodedThread>(m_thread_sp);
+
+ Error err = m_trace.OnThreadBufferRead(
+ m_thread_sp->GetID(), [&](llvm::ArrayRef<uint8_t> data) {
+ return DecodeSingleTraceForThread(*decoded_thread_sp, m_trace,
+ data);
+ });
+
+ if (err)
+ return std::move(err);
+ return decoded_thread_sp;
+ });
}
///
/// \return
/// A \a DecodedThread instance.
- DecodedThreadSP Decode();
+ llvm::Expected<DecodedThreadSP> Decode();
ThreadDecoder(const ThreadDecoder &other) = delete;
ThreadDecoder &operator=(const ThreadDecoder &other) = delete;
private:
- DecodedThreadSP DoDecode();
+ llvm::Expected<DecodedThreadSP> DoDecode();
lldb::ThreadSP m_thread_sp;
TraceIntelPT &m_trace;
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))
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;
}
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<uint64_t>
-TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) {
+TraceCursorIntelPT::GetCounter(lldb::TraceCounter counter_type) const {
switch (counter_type) {
case lldb::eTraceCounterTSC:
if (m_tsc_range)
}
}
-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) {
}
bool TraceCursorIntelPT::HasId(lldb::user_id_t id) const {
- return id < m_decoded_thread_sp->GetInstructionsCount();
+ return static_cast<int64_t>(id) < m_decoded_thread_sp->GetItemsCount();
}
user_id_t TraceCursorIntelPT::GetId() const { return m_pos; }
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<uint64_t> GetCounter(lldb::TraceCounter counter_type) override;
+ llvm::Optional<uint64_t>
+ 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;
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();
: Trace(traced_processes, bundle_description.GetCpuIds()),
m_cpu_info(bundle_description.cpu_info) {}
-DecodedThreadSP TraceIntelPT::Decode(Thread &thread) {
+Expected<DecodedThreadSP> TraceIntelPT::Decode(Thread &thread) {
if (const char *error = RefreshLiveProcessState())
- return std::make_shared<DecodedThread>(
- thread.shared_from_this(),
- createStringError(inconvertibleErrorCode(), error));
+ return createStringError(inconvertibleErrorCode(), error);
Storage &storage = GetUpdatedStorage();
if (storage.multicpu_decoder)
auto it = storage.thread_decoders.find(thread.GetID());
if (it == storage.thread_decoders.end())
- return std::make_shared<DecodedThread>(
- 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<lldb::TraceCursorUP>
+TraceIntelPT::CreateNewCursor(Thread &thread) {
+ if (Expected<DecodedThreadSP> decoded_thread = Decode(thread))
+ return decoded_thread.get()->CreateNewCursor();
+ else
+ return decoded_thread.takeError();
}
void TraceIntelPT::DumpTraceInfo(Thread &thread, Stream &s, bool verbose) {
}
s << "\n";
+ Expected<DecodedThreadSP> 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<Optional<uint64_t>> raw_size_or_error = GetRawTraceSize(thread);
if (!raw_size_or_error) {
s.Format(" {0}\n", toString(raw_size_or_error.takeError()));
}
Optional<uint64_t> 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)
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
// 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);
}
}
{
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 :
llvm::StringRef GetSchema() override;
- lldb::TraceCursorUP GetCursor(Thread &thread) override;
+ llvm::Expected<lldb::TraceCursorUP> CreateNewCursor(Thread &thread) override;
void DumpTraceInfo(Thread &thread, Stream &s, bool verbose) override;
///
/// \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<DecodedThreadSP> Decode(Thread &thread);
/// We package all the data that can change upon process stops to make sure
/// this contract is very visible.
return m_tids.count(tid);
}
-DecodedThreadSP TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {
+Expected<DecodedThreadSP> TraceIntelPTMultiCpuDecoder::Decode(Thread &thread) {
if (Error err = CorrelateContextSwitchesAndIntelPtTraces())
- return std::make_shared<DecodedThread>(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;
[&](const DenseMap<cpu_id_t, ArrayRef<uint8_t>> &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;
/// \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<DecodedThreadSP> Decode(Thread &thread);
/// \return
/// \b true if the given \p tid is managed by this decoder, regardless of
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 =
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()) {
}
}
}
+ */
}
void HTRBlockMetadata::MergeMetadata(
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<TraceCursorUP> 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 {
Trace.cpp
TraceCursor.cpp
TraceExporter.cpp
- TraceInstructionDumper.cpp
+ TraceDumper.cpp
UnixSignals.cpp
UnwindAssembly.cpp
UnwindLLDB.cpp
#include "lldb/Target/TraceCursor.h"
#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Trace.h"
using namespace lldb;
using namespace lldb_private;
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<void(lldb::TraceEvents event)> callback) {
- while (events) {
- TraceEvents event = static_cast<TraceEvents>(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";
}
}
-//===-- 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.
//
//===----------------------------------------------------------------------===//
-#include "lldb/Target/TraceInstructionDumper.h"
+#include "lldb/Target/TraceDumper.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/CompileUnit.h"
/// \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();
}
/// - 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;
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,
}
}
- 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,
}
*/
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) {
~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::string>(std::to_string(*insn.tsc)) : None);
+ item.tsc ? Optional<std::string>(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);
}
}
});
private:
Stream &m_s;
- TraceInstructionDumperOptions m_options;
+ TraceDumperOptions m_options;
json::OStream m_j;
};
-static std::unique_ptr<TraceInstructionDumper::OutputWriter>
-CreateWriter(Stream &s, const TraceInstructionDumperOptions &options) {
+static std::unique_ptr<TraceDumper::OutputWriter>
+CreateWriter(Stream &s, const TraceDumperOptions &options, Thread &thread) {
if (options.json)
- return std::unique_ptr<TraceInstructionDumper::OutputWriter>(
+ return std::unique_ptr<TraceDumper::OutputWriter>(
new OutputWriterJSON(s, options));
else
- return std::unique_ptr<TraceInstructionDumper::OutputWriter>(
- new OutputWriterCLI(s, options));
+ return std::unique_ptr<TraceDumper::OutputWriter>(
+ 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);
}
}
-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,
/// Find the disassembler for the given address reusing the previous
/// instruction's disassembler when possible.
static std::tuple<DisassemblerSP, InstructionSP>
-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 =
: InstructionSP());
}
-Optional<lldb::user_id_t>
-TraceInstructionDumper::DumpInstructions(size_t count) {
+Optional<lldb::user_id_t> 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<lldb::user_id_t> 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;
}
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'''],
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=['''[
"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"
}
]'''])
"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"
}
]''')
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,
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 " +
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",
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
}
]'''])
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"
}
]'''])
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
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'''])
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 .*'''])
error=True)
- def testHtrBasicSuperBlockPassFullCheck(self):
+ def _testHtrBasicSuperBlockPassFullCheck(self):
'''
Test the BasicSuperBlock pass of HTR.
# 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.
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
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
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
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'''])
# 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']))
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 .* '''])
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']))
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']))
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()
# 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