1 //===-- TraceInstructionDumper.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 "lldb/Target/TraceInstructionDumper.h"
10
11 #include "lldb/Core/Module.h"
12 #include "lldb/Symbol/Function.h"
13 #include "lldb/Target/ExecutionContext.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/SectionLoadList.h"
16
17 using namespace lldb;
18 using namespace lldb_private;
19 using namespace llvm;
20
TraceInstructionDumper(lldb::TraceCursorUP && cursor_up,int initial_index,bool raw,bool show_tsc)21 TraceInstructionDumper::TraceInstructionDumper(lldb::TraceCursorUP &&cursor_up,
22 int initial_index, bool raw,
23 bool show_tsc)
24 : m_cursor_up(std::move(cursor_up)), m_index(initial_index), m_raw(raw),
25 m_show_tsc(show_tsc) {}
26
27 /// \return
28 /// Return \b true if the cursor could move one step.
TryMoveOneStep()29 bool TraceInstructionDumper::TryMoveOneStep() {
30 if (!m_cursor_up->Next()) {
31 SetNoMoreData();
32 return false;
33 }
34 m_index += m_cursor_up->IsForwards() ? 1 : -1;
35 return true;
36 }
37
38 /// \return
39 /// The number of characters that would be needed to print the given
40 /// integer.
GetNumberOfChars(int num)41 static int GetNumberOfChars(int num) {
42 if (num == 0)
43 return 1;
44 return (num < 0 ? 1 : 0) + static_cast<int>(log10(abs(num))) + 1;
45 }
46
47 /// Helper struct that holds symbol, disassembly and address information of an
48 /// instruction.
49 struct InstructionSymbolInfo {
50 SymbolContext sc;
51 Address address;
52 lldb::addr_t load_address;
53 lldb::DisassemblerSP disassembler;
54 lldb::InstructionSP instruction;
55 lldb_private::ExecutionContext exe_ctx;
56 };
57
58 // This custom LineEntry validator is neded because some line_entries have
59 // 0 as line, which is meaningless. Notice that LineEntry::IsValid only
60 // checks that line is not LLDB_INVALID_LINE_NUMBER, i.e. UINT32_MAX.
IsLineEntryValid(const LineEntry & line_entry)61 static bool IsLineEntryValid(const LineEntry &line_entry) {
62 return line_entry.IsValid() && line_entry.line > 0;
63 }
64
65 /// \return
66 /// \b true if the provided line entries match line, column and source file.
67 /// This function assumes that the line entries are valid.
FileLineAndColumnMatches(const LineEntry & a,const LineEntry & b)68 static bool FileLineAndColumnMatches(const LineEntry &a, const LineEntry &b) {
69 if (a.line != b.line)
70 return false;
71 if (a.column != b.column)
72 return false;
73 return a.file == b.file;
74 }
75
76 /// Compare the symbol contexts of the provided \a InstructionSymbolInfo
77 /// objects.
78 ///
79 /// \return
80 /// \a true if both instructions belong to the same scope level analized
81 /// in the following order:
82 /// - module
83 /// - symbol
84 /// - function
85 /// - line
86 static bool
IsSameInstructionSymbolContext(const InstructionSymbolInfo & prev_insn,const InstructionSymbolInfo & insn)87 IsSameInstructionSymbolContext(const InstructionSymbolInfo &prev_insn,
88 const InstructionSymbolInfo &insn) {
89 // module checks
90 if (insn.sc.module_sp != prev_insn.sc.module_sp)
91 return false;
92
93 // symbol checks
94 if (insn.sc.symbol != prev_insn.sc.symbol)
95 return false;
96
97 // function checks
98 if (!insn.sc.function && !prev_insn.sc.function)
99 return true;
100 else if (insn.sc.function != prev_insn.sc.function)
101 return false;
102
103 // line entry checks
104 const bool curr_line_valid = IsLineEntryValid(insn.sc.line_entry);
105 const bool prev_line_valid = IsLineEntryValid(prev_insn.sc.line_entry);
106 if (curr_line_valid && prev_line_valid)
107 return FileLineAndColumnMatches(insn.sc.line_entry,
108 prev_insn.sc.line_entry);
109 return curr_line_valid == prev_line_valid;
110 }
111
112 /// Dump the symbol context of the given instruction address if it's different
113 /// from the symbol context of the previous instruction in the trace.
114 ///
115 /// \param[in] prev_sc
116 /// The symbol context of the previous instruction in the trace.
117 ///
118 /// \param[in] address
119 /// The address whose symbol information will be dumped.
120 ///
121 /// \return
122 /// The symbol context of the current address, which might differ from the
123 /// previous one.
124 static void
DumpInstructionSymbolContext(Stream & s,Optional<InstructionSymbolInfo> prev_insn,InstructionSymbolInfo & insn)125 DumpInstructionSymbolContext(Stream &s,
126 Optional<InstructionSymbolInfo> prev_insn,
127 InstructionSymbolInfo &insn) {
128 if (prev_insn && IsSameInstructionSymbolContext(*prev_insn, insn))
129 return;
130
131 s.Printf(" ");
132
133 if (!insn.sc.module_sp)
134 s.Printf("(none)");
135 else if (!insn.sc.function && !insn.sc.symbol)
136 s.Printf("%s`(none)",
137 insn.sc.module_sp->GetFileSpec().GetFilename().AsCString());
138 else
139 insn.sc.DumpStopContext(&s, insn.exe_ctx.GetTargetPtr(), insn.address,
140 /*show_fullpath=*/false,
141 /*show_module=*/true, /*show_inlined_frames=*/false,
142 /*show_function_arguments=*/true,
143 /*show_function_name=*/true);
144 s.Printf("\n");
145 }
146
DumpInstructionDisassembly(Stream & s,InstructionSymbolInfo & insn)147 static void DumpInstructionDisassembly(Stream &s, InstructionSymbolInfo &insn) {
148 if (!insn.instruction)
149 return;
150 s.Printf(" ");
151 insn.instruction->Dump(&s, /*show_address=*/false, /*show_bytes=*/false,
152 /*max_opcode_byte_size=*/0, &insn.exe_ctx, &insn.sc,
153 /*prev_sym_ctx=*/nullptr,
154 /*disassembly_addr_format=*/nullptr,
155 /*max_address_text_size=*/0);
156 }
157
SetNoMoreData()158 void TraceInstructionDumper::SetNoMoreData() { m_no_more_data = true; }
159
HasMoreData()160 bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; }
161
DumpInstructions(Stream & s,size_t count)162 void TraceInstructionDumper::DumpInstructions(Stream &s, size_t count) {
163 ThreadSP thread_sp = m_cursor_up->GetExecutionContextRef().GetThreadSP();
164 if (!thread_sp) {
165 s.Printf("invalid thread");
166 return;
167 }
168
169 s.Printf("thread #%u: tid = %" PRIu64 "\n", thread_sp->GetIndexID(),
170 thread_sp->GetID());
171
172 int digits_count = GetNumberOfChars(
173 m_cursor_up->IsForwards() ? m_index + count - 1 : m_index - count + 1);
174 bool was_prev_instruction_an_error = false;
175
176 auto printMissingInstructionsMessage = [&]() {
177 s.Printf(" ...missing instructions\n");
178 };
179
180 auto printInstructionIndex = [&]() {
181 s.Printf(" [%*d] ", digits_count, m_index);
182
183 if (m_show_tsc) {
184 s.Printf("[tsc=");
185
186 if (Optional<uint64_t> timestamp = m_cursor_up->GetTimestampCounter())
187 s.Printf("0x%016" PRIx64, *timestamp);
188 else
189 s.Printf("unavailable");
190
191 s.Printf("] ");
192 }
193 };
194
195 InstructionSymbolInfo prev_insn_info;
196
197 Target &target = thread_sp->GetProcess()->GetTarget();
198 ExecutionContext exe_ctx;
199 target.CalculateExecutionContext(exe_ctx);
200 const ArchSpec &arch = target.GetArchitecture();
201
202 // Find the symbol context for the given address reusing the previous
203 // instruction's symbol context when possible.
204 auto calculateSymbolContext = [&](const Address &address) {
205 AddressRange range;
206 if (prev_insn_info.sc.GetAddressRange(eSymbolContextEverything, 0,
207 /*inline_block_range*/ false,
208 range) &&
209 range.Contains(address))
210 return prev_insn_info.sc;
211
212 SymbolContext sc;
213 address.CalculateSymbolContext(&sc, eSymbolContextEverything);
214 return sc;
215 };
216
217 // Find the disassembler for the given address reusing the previous
218 // instruction's disassembler when possible.
219 auto calculateDisass = [&](const Address &address, const SymbolContext &sc) {
220 if (prev_insn_info.disassembler) {
221 if (InstructionSP instruction =
222 prev_insn_info.disassembler->GetInstructionList()
223 .GetInstructionAtAddress(address))
224 return std::make_tuple(prev_insn_info.disassembler, instruction);
225 }
226
227 if (sc.function) {
228 if (DisassemblerSP disassembler =
229 sc.function->GetInstructions(exe_ctx, nullptr)) {
230 if (InstructionSP instruction =
231 disassembler->GetInstructionList().GetInstructionAtAddress(
232 address))
233 return std::make_tuple(disassembler, instruction);
234 }
235 }
236 // We fallback to a single instruction disassembler
237 AddressRange range(address, arch.GetMaximumOpcodeByteSize());
238 DisassemblerSP disassembler =
239 Disassembler::DisassembleRange(arch, /*plugin_name*/ nullptr,
240 /*flavor*/ nullptr, target, range);
241 return std::make_tuple(disassembler,
242 disassembler ? disassembler->GetInstructionList()
243 .GetInstructionAtAddress(address)
244 : InstructionSP());
245 };
246
247 for (size_t i = 0; i < count; i++) {
248 if (!HasMoreData()) {
249 s.Printf(" no more data\n");
250 break;
251 }
252
253 if (Error err = m_cursor_up->GetError()) {
254 if (!m_cursor_up->IsForwards() && !was_prev_instruction_an_error)
255 printMissingInstructionsMessage();
256
257 was_prev_instruction_an_error = true;
258
259 printInstructionIndex();
260 s << toString(std::move(err));
261 } else {
262 if (m_cursor_up->IsForwards() && was_prev_instruction_an_error)
263 printMissingInstructionsMessage();
264
265 was_prev_instruction_an_error = false;
266
267 InstructionSymbolInfo insn_info;
268
269 if (!m_raw) {
270 insn_info.load_address = m_cursor_up->GetLoadAddress();
271 insn_info.exe_ctx = exe_ctx;
272 insn_info.address.SetLoadAddress(insn_info.load_address, &target);
273 insn_info.sc = calculateSymbolContext(insn_info.address);
274 std::tie(insn_info.disassembler, insn_info.instruction) =
275 calculateDisass(insn_info.address, insn_info.sc);
276
277 DumpInstructionSymbolContext(s, prev_insn_info, insn_info);
278 }
279
280 printInstructionIndex();
281 s.Printf("0x%016" PRIx64, m_cursor_up->GetLoadAddress());
282
283 if (!m_raw)
284 DumpInstructionDisassembly(s, insn_info);
285
286 prev_insn_info = insn_info;
287 }
288
289 s.Printf("\n");
290 TryMoveOneStep();
291 }
292 }
293