1 //===-- Trace.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/Trace.h" 10 11 #include "llvm/Support/Format.h" 12 13 #include "lldb/Core/Module.h" 14 #include "lldb/Core/PluginManager.h" 15 #include "lldb/Symbol/Function.h" 16 #include "lldb/Target/Process.h" 17 #include "lldb/Target/SectionLoadList.h" 18 #include "lldb/Target/Thread.h" 19 #include "lldb/Utility/Stream.h" 20 21 using namespace lldb; 22 using namespace lldb_private; 23 using namespace llvm; 24 25 // Helper structs used to extract the type of a trace session json without 26 // having to parse the entire object. 27 28 struct JSONSimplePluginSettings { 29 std::string type; 30 }; 31 32 struct JSONSimpleTraceSession { 33 JSONSimplePluginSettings trace; 34 }; 35 36 namespace llvm { 37 namespace json { 38 39 bool fromJSON(const Value &value, JSONSimplePluginSettings &plugin_settings, 40 Path path) { 41 json::ObjectMapper o(value, path); 42 return o && o.map("type", plugin_settings.type); 43 } 44 45 bool fromJSON(const Value &value, JSONSimpleTraceSession &session, Path path) { 46 json::ObjectMapper o(value, path); 47 return o && o.map("trace", session.trace); 48 } 49 50 } // namespace json 51 } // namespace llvm 52 53 static Error createInvalidPlugInError(StringRef plugin_name) { 54 return createStringError( 55 std::errc::invalid_argument, 56 "no trace plug-in matches the specified type: \"%s\"", 57 plugin_name.data()); 58 } 59 60 Expected<lldb::TraceSP> Trace::FindPlugin(Debugger &debugger, 61 const json::Value &trace_session_file, 62 StringRef session_file_dir) { 63 JSONSimpleTraceSession json_session; 64 json::Path::Root root("traceSession"); 65 if (!json::fromJSON(trace_session_file, json_session, root)) 66 return root.getError(); 67 68 ConstString plugin_name(json_session.trace.type); 69 if (auto create_callback = PluginManager::GetTraceCreateCallback(plugin_name)) 70 return create_callback(trace_session_file, session_file_dir, debugger); 71 72 return createInvalidPlugInError(json_session.trace.type); 73 } 74 75 Expected<StringRef> Trace::FindPluginSchema(StringRef name) { 76 ConstString plugin_name(name); 77 StringRef schema = PluginManager::GetTraceSchema(plugin_name); 78 if (!schema.empty()) 79 return schema; 80 81 return createInvalidPlugInError(name); 82 } 83 84 static int GetNumberOfDigits(size_t num) { 85 return num == 0 ? 1 : static_cast<int>(log10(num)) + 1; 86 } 87 88 /// Dump the symbol context of the given instruction address if it's different 89 /// from the symbol context of the previous instruction in the trace. 90 /// 91 /// \param[in] prev_sc 92 /// The symbol context of the previous instruction in the trace. 93 /// 94 /// \param[in] address 95 /// The address whose symbol information will be dumped. 96 /// 97 /// \return 98 /// The symbol context of the current address, which might differ from the 99 /// previous one. 100 static SymbolContext DumpSymbolContext(Stream &s, const SymbolContext &prev_sc, 101 Target &target, const Address &address) { 102 AddressRange range; 103 if (prev_sc.GetAddressRange(eSymbolContextEverything, 0, 104 /*inline_block_range*/ false, range) && 105 range.ContainsFileAddress(address)) 106 return prev_sc; 107 108 SymbolContext sc; 109 address.CalculateSymbolContext(&sc, eSymbolContextEverything); 110 111 if (!prev_sc.module_sp && !sc.module_sp) 112 return sc; 113 if (prev_sc.module_sp == sc.module_sp && !sc.function && !sc.symbol && 114 !prev_sc.function && !prev_sc.symbol) 115 return sc; 116 117 s.Printf(" "); 118 119 if (!sc.module_sp) 120 s.Printf("(none)"); 121 else if (!sc.function && !sc.symbol) 122 s.Printf("%s`(none)", 123 sc.module_sp->GetFileSpec().GetFilename().AsCString()); 124 else 125 sc.DumpStopContext(&s, &target, address, /*show_fullpath*/ false, 126 /*show_module*/ true, /*show_inlined_frames*/ false, 127 /*show_function_arguments*/ true, 128 /*show_function_name*/ true, 129 /*show_inline_callsite_line_info*/ false); 130 s.Printf("\n"); 131 return sc; 132 } 133 134 /// Dump an instruction given by its address using a given disassembler, unless 135 /// the instruction is not present in the disassembler. 136 /// 137 /// \param[in] disassembler 138 /// A disassembler containing a certain instruction list. 139 /// 140 /// \param[in] address 141 /// The address of the instruction to dump. 142 /// 143 /// \return 144 /// \b true if the information could be dumped, \b false otherwise. 145 static bool TryDumpInstructionInfo(Stream &s, 146 const DisassemblerSP &disassembler, 147 const ExecutionContext &exe_ctx, 148 const Address &address) { 149 if (!disassembler) 150 return false; 151 152 if (InstructionSP instruction = 153 disassembler->GetInstructionList().GetInstructionAtAddress(address)) { 154 instruction->Dump(&s, /*show_address*/ false, /*show_bytes*/ false, 155 /*max_opcode_byte_size*/ 0, &exe_ctx, 156 /*sym_ctx*/ nullptr, /*prev_sym_ctx*/ nullptr, 157 /*disassembly_addr_format*/ nullptr, 158 /*max_address_text_size*/ 0); 159 return true; 160 } 161 162 return false; 163 } 164 165 /// Dump an instruction instruction given by its address. 166 /// 167 /// \param[in] prev_disassembler 168 /// The disassembler that was used to dump the previous instruction in the 169 /// trace. It is useful to avoid recomputations. 170 /// 171 /// \param[in] address 172 /// The address of the instruction to dump. 173 /// 174 /// \return 175 /// A disassembler that contains the given instruction, which might differ 176 /// from the previous disassembler. 177 static DisassemblerSP 178 DumpInstructionInfo(Stream &s, const SymbolContext &sc, 179 const DisassemblerSP &prev_disassembler, 180 ExecutionContext &exe_ctx, const Address &address) { 181 // We first try to use the previous disassembler 182 if (TryDumpInstructionInfo(s, prev_disassembler, exe_ctx, address)) 183 return prev_disassembler; 184 185 // Now we try using the current function's disassembler 186 if (sc.function) { 187 DisassemblerSP disassembler = 188 sc.function->GetInstructions(exe_ctx, nullptr, true); 189 if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) 190 return disassembler; 191 } 192 193 // We fallback to disassembly one instruction 194 Target &target = exe_ctx.GetTargetRef(); 195 const ArchSpec &arch = target.GetArchitecture(); 196 AddressRange range(address, arch.GetMaximumOpcodeByteSize() * 1); 197 DisassemblerSP disassembler = Disassembler::DisassembleRange( 198 arch, /*plugin_name*/ nullptr, 199 /*flavor*/ nullptr, target, range, /*prefer_file_cache*/ true); 200 if (TryDumpInstructionInfo(s, disassembler, exe_ctx, address)) 201 return disassembler; 202 return nullptr; 203 } 204 205 void Trace::DumpTraceInstructions(Thread &thread, Stream &s, size_t count, 206 size_t end_position, bool raw) { 207 size_t instructions_count = GetInstructionCount(thread); 208 s.Printf("thread #%u: tid = %" PRIu64 ", total instructions = %zu\n", 209 thread.GetIndexID(), thread.GetID(), instructions_count); 210 211 if (count == 0 || end_position >= instructions_count) 212 return; 213 214 size_t start_position = 215 end_position + 1 < count ? 0 : end_position + 1 - count; 216 217 int digits_count = GetNumberOfDigits(end_position); 218 auto printInstructionIndex = [&](size_t index) { 219 s.Printf(" [%*zu] ", digits_count, index); 220 }; 221 222 bool was_prev_instruction_an_error = false; 223 Target &target = thread.GetProcess()->GetTarget(); 224 225 SymbolContext sc; 226 DisassemblerSP disassembler; 227 ExecutionContext exe_ctx; 228 target.CalculateExecutionContext(exe_ctx); 229 230 TraverseInstructions( 231 thread, start_position, TraceDirection::Forwards, 232 [&](size_t index, Expected<lldb::addr_t> load_address) -> bool { 233 if (load_address) { 234 // We print an empty line after a sequence of errors to show more 235 // clearly that there's a gap in the trace 236 if (was_prev_instruction_an_error) 237 s.Printf(" ...missing instructions\n"); 238 239 Address address; 240 if (!raw) { 241 target.GetSectionLoadList().ResolveLoadAddress(*load_address, 242 address); 243 244 sc = DumpSymbolContext(s, sc, target, address); 245 } 246 247 printInstructionIndex(index); 248 s.Printf("0x%016" PRIx64 " ", *load_address); 249 250 if (!raw) { 251 disassembler = 252 DumpInstructionInfo(s, sc, disassembler, exe_ctx, address); 253 } 254 255 was_prev_instruction_an_error = false; 256 } else { 257 printInstructionIndex(index); 258 s << toString(load_address.takeError()); 259 was_prev_instruction_an_error = true; 260 if (!raw) 261 sc = SymbolContext(); 262 } 263 264 s.Printf("\n"); 265 266 return index < end_position; 267 }); 268 } 269