Skip to content

Commit

Permalink
[trace] Make events first class items in the trace cursor and rework …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
walter-erquinigo committed Jun 29, 2022
1 parent 7cbfb4e commit a7d6c3e
Show file tree
Hide file tree
Showing 31 changed files with 789 additions and 933 deletions.
6 changes: 3 additions & 3 deletions lldb/include/lldb/Target/Trace.h
Expand Up @@ -167,9 +167,9 @@ class Trace : public PluginInterface,
///
/// \return
/// A \a TraceCursorUP. If the thread is not traced or its trace
/// information failed to load, the corresponding error is embedded in the
/// trace.
virtual lldb::TraceCursorUP GetCursor(Thread &thread) = 0;
/// information failed to load, an \a llvm::Error is returned.
virtual llvm::Expected<lldb::TraceCursorUP>
CreateNewCursor(Thread &thread) = 0;

/// Dump general info about a given thread's trace. Each Trace plug-in
/// decides which data to show.
Expand Down
140 changes: 63 additions & 77 deletions lldb/include/lldb/Target/TraceCursor.h
Expand Up @@ -15,29 +15,35 @@

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
/// the instruction data in an flexible way that is efficient for the given
/// 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
Expand All @@ -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;
/// }
/// }
Expand Down Expand Up @@ -210,26 +215,45 @@ class TraceCursor {
/// of this cursor.
ExecutionContextRef &GetExecutionContextRef();

/// Instruction or error information
/// Instruction, event or error information
/// \{

/// \return
/// The kind of item the cursor is pointing at.
virtual lldb::TraceItemKind GetItemKind() const = 0;

/// \return
/// Whether the cursor points to an error or not.
virtual bool IsError() = 0;
bool IsError() const;

/// \return
/// The error message the cursor is pointing at.
virtual const char *GetError() const = 0;

/// \return
/// Whether the cursor points to an event or not.
bool IsEvent() const;

/// \return
/// The specific kind of event the cursor is pointing at, or \b
/// TraceEvent::eTraceEventNone if the cursor not pointing to an event.
virtual lldb::TraceEvent GetEventType() const = 0;

/// \return
/// A human-readable description of the event this cursor is pointing at.
const char *GetEventTypeAsString() const;

/// \return
/// A human-readable description of the given event.
static const char *EventKindToString(lldb::TraceEvent event_kind);

/// Get the corresponding error message if the cursor points to an error in
/// the trace.
///
/// \return
/// \b nullptr if the cursor is not pointing to an error in
/// the trace. Otherwise return the actual error message.
virtual const char *GetError() = 0;
/// Whether the cursor points to an instruction.
bool IsInstruction() const;

/// \return
/// The load address of the instruction the cursor is pointing at. If the
/// cursor points to an error in the trace, return \b
/// LLDB_INVALID_ADDRESS.
virtual lldb::addr_t GetLoadAddress() = 0;
/// The load address of the instruction the cursor is pointing at.
virtual lldb::addr_t GetLoadAddress() const = 0;

/// Get the hardware counter of a given type associated with the current
/// instruction. Each architecture might support different counters. It might
Expand All @@ -240,52 +264,14 @@ class TraceCursor {
/// The counter type.
/// \return
/// The value of the counter or \b llvm::None if not available.
virtual llvm::Optional<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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -55,12 +55,13 @@ class TraceInstructionDumper {
lldb_private::ExecutionContext exe_ctx;
};

/// Helper struct that holds all the information we know about an instruction
struct InstructionEntry {
/// Helper struct that holds all the information we know about a trace item
struct TraceItem {
lldb::user_id_t id;
lldb::addr_t load_address;
llvm::Optional<uint64_t> tsc;
llvm::Optional<llvm::StringRef> error;
llvm::Optional<lldb::TraceEvent> event;
llvm::Optional<SymbolInfo> symbol_info;
llvm::Optional<SymbolInfo> prev_symbol_info;
};
Expand All @@ -71,14 +72,11 @@ class TraceInstructionDumper {
public:
virtual ~OutputWriter() = default;

/// Indicate a user-level info message. It's not part of the actual trace.
virtual void InfoMessage(llvm::StringRef text) {}
/// Notify this writer that the cursor ran out of data.
virtual void NoMoreData() {}

/// Dump a trace event.
virtual void Event(llvm::StringRef text) = 0;

/// Dump an instruction or a trace error.
virtual void Instruction(const InstructionEntry &insn) = 0;
/// Dump a trace item (instruction, error or event).
virtual void TraceItem(const TraceItem &item) = 0;
};

/// Create a instruction dumper for the cursor.
Expand All @@ -91,8 +89,8 @@ class TraceInstructionDumper {
///
/// \param[in] options
/// Additional options for configuring the dumping.
TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up, Stream &s,
const TraceInstructionDumperOptions &options);
TraceDumper(lldb::TraceCursorUP &&cursor_up, Stream &s,
const TraceDumperOptions &options);

/// Dump \a count instructions of the thread trace starting at the current
/// cursor position.
Expand All @@ -109,14 +107,11 @@ class TraceInstructionDumper {
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;
};

Expand Down
20 changes: 13 additions & 7 deletions lldb/include/lldb/lldb-enumerations.h
Expand 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

Expand Down
20 changes: 13 additions & 7 deletions lldb/source/Commands/CommandObjectThread.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -2208,7 +2208,7 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed {
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)
Expand Down Expand Up @@ -2272,8 +2272,14 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed {
m_options.m_dumper_options.id = m_last_id;
}

TraceCursorUP cursor_up =
m_exe_ctx.GetTargetSP()->GetTrace()->GetCursor(*thread_sp);
llvm::Expected<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)) {
Expand All @@ -2295,9 +2301,9 @@ class CommandObjectTraceDumpInstructions : public CommandObjectParsed {
cursor_up->Seek(1, TraceCursor::SeekType::End);
}

TraceInstructionDumper dumper(
std::move(cursor_up), out_file ? *out_file : result.GetOutputStream(),
m_options.m_dumper_options);
TraceDumper dumper(std::move(cursor_up),
out_file ? *out_file : result.GetOutputStream(),
m_options.m_dumper_options);

m_last_id = dumper.DumpInstructions(m_options.m_count);
return true;
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Commands/Options.td
Expand Up @@ -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.">;
Expand Down

0 comments on commit a7d6c3e

Please sign in to comment.