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