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 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. 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. 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. 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. 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 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 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 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 158 void TraceInstructionDumper::SetNoMoreData() { m_no_more_data = true; } 159 160 bool TraceInstructionDumper::HasMoreData() { return !m_no_more_data; } 161 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