Skip to content

Commit cfd96f0

Browse files
Walter Erquinigowalter-erquinigo
authored andcommitted
[trace][intel-pt] Implement the basic decoding functionality
Depends on D89408. This diff finally implements trace decoding! The current interface is $ trace load /path/to/trace/session/file.json $ thread trace dump instructions thread #1: tid = 3842849, total instructions = 22 [ 0] 0x40052d [ 1] 0x40052d ... [19] 0x400521 $ # simply enter, which is a repeat command [20] 0x40052d [21] 0x400529 ... This doesn't do any disassembly, which will be done in the next diff. Changes: - Added an IntelPTDecoder class, that is a wrapper for libipt, which is the actual library that performs the decoding. - Added TraceThreadDecoder class that decodes traces and memoizes the result to avoid repeating the decoding step. - Added a DecodedThread class, which represents the output from decoding and that for the time being only stores the list of reconstructed instructions. Later it'll contain the function call hierarchy, which will enable reconstructing backtraces. - Added basic APIs for accessing the trace in Trace.h: - GetInstructionCount, which counts the number of instructions traced for a given thread - IsTraceFailed, which returns an Error if decoding a thread failed - ForEachInstruction, which iterates on the instructions traced for a given thread, concealing the internal storage of threads, as plug-ins can decide to generate the instructions on the fly or to store them all in a vector, like I do. - DumpTraceInstructions was updated to print the instructions or show an error message if decoding was impossible. - Tests included Differential Revision: https://reviews.llvm.org/D89283
1 parent b874575 commit cfd96f0

31 files changed

+1235
-107
lines changed

lldb/include/lldb/Core/Disassembler.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,13 @@ class InstructionList {
271271

272272
lldb::InstructionSP GetInstructionAtIndex(size_t idx) const;
273273

274+
/// Get the instruction at the given address.
275+
///
276+
/// \return
277+
/// A valid \a InstructionSP if the address could be found, or null
278+
/// otherwise.
279+
lldb::InstructionSP GetInstructionAtAddress(const Address &addr);
280+
274281
//------------------------------------------------------------------
275282
/// Get the index of the next branch instruction.
276283
///

lldb/include/lldb/Symbol/SymbolContext.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,19 @@ class SymbolContext {
139139
/// be printed. In disassembly formatting, where we want a format
140140
/// like "<*+36>", this should be false and "*" will be printed
141141
/// instead.
142+
///
143+
/// \param[in] show_inline_callsite_line_info
144+
/// When processing an inline block, the line info of the callsite
145+
/// is dumped if this flag is \b true, otherwise the line info
146+
/// of the actual inlined function is dumped.
147+
///
148+
/// \return
149+
/// \b true if some text was dumped, \b false otherwise.
142150
bool DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
143151
const Address &so_addr, bool show_fullpaths,
144152
bool show_module, bool show_inlined_frames,
145-
bool show_function_arguments,
146-
bool show_function_name) const;
153+
bool show_function_arguments, bool show_function_name,
154+
bool show_inline_callsite_line_info = true) const;
147155

148156
/// Get the address range contained within a symbol context.
149157
///

lldb/include/lldb/Target/Trace.h

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ namespace lldb_private {
3535
class Trace : public PluginInterface,
3636
public std::enable_shared_from_this<Trace> {
3737
public:
38+
enum class TraceDirection {
39+
Forwards = 0,
40+
Backwards,
41+
};
42+
3843
/// Dump the trace data that this plug-in has access to.
3944
///
4045
/// This function will dump all of the trace data for all threads in a user
@@ -98,12 +103,24 @@ class Trace : public PluginInterface,
98103
/// The JSON schema of this Trace plug-in.
99104
virtual llvm::StringRef GetSchema() = 0;
100105

101-
/// Dump \a count instructions of the given thread's \a Trace starting at the
102-
/// \a start_position position in reverse order.
106+
/// Each decoded thread contains a cursor to the current position the user is
107+
/// stopped at. When reverse debugging, each operation like reverse-next or
108+
/// reverse-continue will move this cursor, which is then picked by any
109+
/// subsequent dump or reverse operation.
110+
///
111+
/// The initial position for this cursor is the last element of the thread,
112+
/// which is the most recent chronologically.
113+
///
114+
/// \return
115+
/// The current position of the thread's trace or \b 0 if empty.
116+
virtual size_t GetCursorPosition(const Thread &thread) = 0;
117+
118+
/// Dump \a count instructions of the given thread's trace ending at the
119+
/// given \a end_position position.
103120
///
104-
/// The instructions are indexed in reverse order, which means that the \a
105-
/// start_position 0 represents the last instruction of the trace
106-
/// chronologically.
121+
/// The instructions are printed along with their indices or positions, which
122+
/// are increasing chronologically. This means that the \a index 0 represents
123+
/// the oldest instruction of the trace chronologically.
107124
///
108125
/// \param[in] thread
109126
/// The thread whose trace will be dumped.
@@ -114,10 +131,54 @@ class Trace : public PluginInterface,
114131
/// \param[in] count
115132
/// The number of instructions to print.
116133
///
117-
/// \param[in] start_position
118-
/// The position of the first instruction to print.
134+
/// \param[in] end_position
135+
/// The position of the last instruction to print.
136+
///
137+
/// \param[in] raw
138+
/// Dump only instruction addresses without disassembly nor symbol
139+
/// information.
119140
void DumpTraceInstructions(Thread &thread, Stream &s, size_t count,
120-
size_t start_position) const;
141+
size_t end_position, bool raw);
142+
143+
/// Run the provided callback on the instructions of the trace of the given
144+
/// thread.
145+
///
146+
/// The instructions will be traversed starting at the given \a position
147+
/// sequentially until the callback returns \b false, in which case no more
148+
/// instructions are inspected.
149+
///
150+
/// The purpose of this method is to allow inspecting traced instructions
151+
/// without exposing the internal representation of how they are stored on
152+
/// memory.
153+
///
154+
/// \param[in] thread
155+
/// The thread whose trace will be traversed.
156+
///
157+
/// \param[in] position
158+
/// The instruction position to start iterating on.
159+
///
160+
/// \param[in] direction
161+
/// If \b TraceDirection::Forwards, then then instructions will be
162+
/// traversed forwards chronologically, i.e. with incrementing indices. If
163+
/// \b TraceDirection::Backwards, the traversal is done backwards
164+
/// chronologically, i.e. with decrementing indices.
165+
///
166+
/// \param[in] callback
167+
/// The callback to execute on each instruction. If it returns \b false,
168+
/// the iteration stops.
169+
virtual void TraverseInstructions(
170+
const Thread &thread, size_t position, TraceDirection direction,
171+
std::function<bool(size_t index, llvm::Expected<lldb::addr_t> load_addr)>
172+
callback) = 0;
173+
174+
/// Get the number of available instructions in the trace of the given thread.
175+
///
176+
/// \param[in] thread
177+
/// The thread whose trace will be inspected.
178+
///
179+
/// \return
180+
/// The total number of instructions in the trace.
181+
virtual size_t GetInstructionCount(const Thread &thread) = 0;
121182
};
122183

123184
} // namespace lldb_private

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2113,7 +2113,7 @@ def runCmd(self, cmd, msg=None, check=True, trace=False, inHistory=False):
21132113
return status.
21142114
"""
21152115
# Fail fast if 'cmd' is not meaningful.
2116-
if not cmd or len(cmd) == 0:
2116+
if cmd is None:
21172117
raise Exception("Bad 'cmd' parameter encountered")
21182118

21192119
trace = (True if traceAlways else trace)

lldb/source/Commands/CommandObjectThread.cpp

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2200,15 +2200,19 @@ class CommandObjectTraceDumpInstructions
22002200
m_count = count;
22012201
break;
22022202
}
2203-
case 's': {
2204-
int32_t start_position;
2205-
if (option_arg.empty() || option_arg.getAsInteger(0, start_position) ||
2206-
start_position < 0)
2203+
case 'p': {
2204+
int32_t position;
2205+
if (option_arg.empty() || option_arg.getAsInteger(0, position) ||
2206+
position < 0)
22072207
error.SetErrorStringWithFormat(
22082208
"invalid integer value for option '%s'",
22092209
option_arg.str().c_str());
22102210
else
2211-
m_start_position = start_position;
2211+
m_position = position;
2212+
break;
2213+
}
2214+
case 'r': {
2215+
m_raw = true;
22122216
break;
22132217
}
22142218
default:
@@ -2219,19 +2223,20 @@ class CommandObjectTraceDumpInstructions
22192223

22202224
void OptionParsingStarting(ExecutionContext *execution_context) override {
22212225
m_count = kDefaultCount;
2222-
m_start_position = kDefaultStartPosition;
2226+
m_position = llvm::None;
2227+
m_raw = false;
22232228
}
22242229

22252230
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
22262231
return llvm::makeArrayRef(g_thread_trace_dump_instructions_options);
22272232
}
22282233

2229-
static const uint32_t kDefaultCount = 20;
2230-
static const uint32_t kDefaultStartPosition = 0;
2234+
static const size_t kDefaultCount = 20;
22312235

22322236
// Instance variables to hold the values for command options.
2233-
uint32_t m_count;
2234-
uint32_t m_start_position;
2237+
size_t m_count;
2238+
llvm::Optional<ssize_t> m_position;
2239+
bool m_raw;
22352240
};
22362241

22372242
CommandObjectTraceDumpInstructions(CommandInterpreter &interpreter)
@@ -2253,30 +2258,24 @@ class CommandObjectTraceDumpInstructions
22532258
uint32_t index) override {
22542259
current_command_args.GetCommandString(m_repeat_command);
22552260
m_create_repeat_command_just_invoked = true;
2261+
m_consecutive_repetitions = 0;
22562262
return m_repeat_command.c_str();
22572263
}
22582264

22592265
protected:
22602266
bool DoExecute(Args &args, CommandReturnObject &result) override {
2267+
if (IsRepeatCommand())
2268+
m_consecutive_repetitions++;
22612269
bool status = CommandObjectIterateOverThreads::DoExecute(args, result);
2262-
PrepareRepeatArguments();
2263-
return status;
2264-
}
22652270

2266-
void PrepareRepeatArguments() {
2267-
m_repeat_start_position = m_options.m_count + GetStartPosition();
22682271
m_create_repeat_command_just_invoked = false;
2272+
return status;
22692273
}
22702274

22712275
bool IsRepeatCommand() {
22722276
return !m_repeat_command.empty() && !m_create_repeat_command_just_invoked;
22732277
}
22742278

2275-
uint32_t GetStartPosition() {
2276-
return IsRepeatCommand() ? m_repeat_start_position
2277-
: m_options.m_start_position;
2278-
}
2279-
22802279
bool HandleOneThread(lldb::tid_t tid, CommandReturnObject &result) override {
22812280
const TraceSP &trace_sp = m_exe_ctx.GetTargetSP()->GetTrace();
22822281
if (!trace_sp) {
@@ -2287,8 +2286,15 @@ class CommandObjectTraceDumpInstructions
22872286
ThreadSP thread_sp =
22882287
m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
22892288

2290-
trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(),
2291-
m_options.m_count, GetStartPosition());
2289+
size_t count = m_options.m_count;
2290+
ssize_t position = m_options.m_position.getValueOr(
2291+
trace_sp->GetCursorPosition(*thread_sp)) -
2292+
m_consecutive_repetitions * count;
2293+
if (position < 0)
2294+
result.SetError("error: no more data");
2295+
else
2296+
trace_sp->DumpTraceInstructions(*thread_sp, result.GetOutputStream(),
2297+
count, position, m_options.m_raw);
22922298
return true;
22932299
}
22942300

@@ -2297,7 +2303,7 @@ class CommandObjectTraceDumpInstructions
22972303
// Repeat command helpers
22982304
std::string m_repeat_command;
22992305
bool m_create_repeat_command_just_invoked;
2300-
uint32_t m_repeat_start_position;
2306+
size_t m_consecutive_repetitions = 0;
23012307
};
23022308

23032309
// CommandObjectMultiwordTraceDump

lldb/source/Commands/Options.td

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,16 +1012,14 @@ let Command = "thread plan list" in {
10121012
let Command = "thread trace dump instructions" in {
10131013
def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
10141014
Arg<"Count">,
1015-
Desc<"The number of instructions to display starting at the current "
1016-
"position in reverse order chronologically.">;
1017-
def thread_trace_dump_instructions_start_position:
1018-
Option<"start-position", "s">,
1015+
Desc<"The number of instructions to display ending at the current position.">;
1016+
def thread_trace_dump_instructions_position : Option<"position", "p">,
10191017
Group<1>,
10201018
Arg<"Index">,
1021-
Desc<"The position of the first instruction to print. Defaults to the "
1022-
"current position, i.e. where the thread is stopped. The instructions are "
1023-
"indexed in reverse order, which means that a start position of 0 refers "
1024-
"to the last instruction chronologically.">;
1019+
Desc<"The position to use instead of the current position of the trace.">;
1020+
def thread_trace_dump_instructions_raw : Option<"raw", "r">,
1021+
Group<1>,
1022+
Desc<"Dump only instruction address without disassembly nor symbol information.">;
10251023
}
10261024

10271025
let Command = "type summary add" in {

lldb/source/Core/Disassembler.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,13 @@ InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {
952952
return inst_sp;
953953
}
954954

955+
InstructionSP InstructionList::GetInstructionAtAddress(const Address &address) {
956+
uint32_t index = GetIndexOfInstructionAtAddress(address);
957+
if (index != UINT32_MAX)
958+
return GetInstructionAtIndex(index);
959+
return nullptr;
960+
}
961+
955962
void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes,
956963
const ExecutionContext *exe_ctx) {
957964
const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize();

lldb/source/Plugins/Trace/intel-pt/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ include_directories(${LIBIPT_INCLUDE_PATH})
1010
find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH} REQUIRED)
1111

1212
add_lldb_library(lldbPluginTraceIntelPT PLUGIN
13+
DecodedThread.cpp
14+
IntelPTDecoder.cpp
1315
TraceIntelPT.cpp
1416
TraceIntelPTSessionFileParser.cpp
1517

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===-- DecodedThread.cpp -------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DecodedThread.h"
10+
11+
#include "lldb/Utility/StreamString.h"
12+
13+
using namespace lldb_private;
14+
using namespace lldb_private::trace_intel_pt;
15+
using namespace llvm;
16+
17+
char IntelPTError::ID;
18+
19+
IntelPTError::IntelPTError(int libipt_error_code, lldb::addr_t address)
20+
: m_libipt_error_code(libipt_error_code), m_address(address) {
21+
assert(libipt_error_code < 0);
22+
}
23+
24+
void IntelPTError::log(llvm::raw_ostream &OS) const {
25+
const char *libipt_error_message = pt_errstr(pt_errcode(m_libipt_error_code));
26+
if (m_address != LLDB_INVALID_ADDRESS && m_address > 0) {
27+
write_hex(OS, m_address, HexPrintStyle::PrefixLower, 18);
28+
OS << " ";
29+
}
30+
OS << "error: " << libipt_error_message;
31+
}
32+
33+
bool IntelPTInstruction::IsError() const { return (bool)m_error; }
34+
35+
Expected<lldb::addr_t> IntelPTInstruction::GetLoadAddress() const {
36+
if (IsError())
37+
return ToError();
38+
return m_pt_insn.ip;
39+
}
40+
41+
Error IntelPTInstruction::ToError() const {
42+
if (!IsError())
43+
return Error::success();
44+
45+
if (m_error->isA<IntelPTError>())
46+
return make_error<IntelPTError>(static_cast<IntelPTError &>(*m_error));
47+
return make_error<StringError>(m_error->message(),
48+
m_error->convertToErrorCode());
49+
}
50+
51+
size_t DecodedThread::GetLastPosition() const {
52+
return m_instructions.empty() ? 0 : m_instructions.size() - 1;
53+
}
54+
55+
ArrayRef<IntelPTInstruction> DecodedThread::GetInstructions() const {
56+
return makeArrayRef(m_instructions);
57+
}
58+
59+
size_t DecodedThread::GetCursorPosition() const { return m_position; }
60+
61+
size_t DecodedThread::SetCursorPosition(size_t new_position) {
62+
m_position = std::min(new_position, GetLastPosition());
63+
return m_position;
64+
}

0 commit comments

Comments
 (0)