1 //===-- TraceIntelPTBundleLoader.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 "TraceIntelPTBundleLoader.h" 10 11 #include "../common/ThreadPostMortemTrace.h" 12 #include "TraceIntelPT.h" 13 #include "TraceIntelPTConstants.h" 14 #include "TraceIntelPTJSONStructs.h" 15 #include "lldb/Core/Debugger.h" 16 #include "lldb/Core/Module.h" 17 #include "lldb/Target/Process.h" 18 #include "lldb/Target/ProcessTrace.h" 19 #include "lldb/Target/Target.h" 20 #include <optional> 21 22 using namespace lldb; 23 using namespace lldb_private; 24 using namespace lldb_private::trace_intel_pt; 25 using namespace llvm; 26 27 FileSpec TraceIntelPTBundleLoader::NormalizePath(const std::string &path) { 28 FileSpec file_spec(path); 29 if (file_spec.IsRelative()) 30 file_spec.PrependPathComponent(m_bundle_dir); 31 return file_spec; 32 } 33 34 Error TraceIntelPTBundleLoader::ParseModule(Target &target, 35 const JSONModule &module) { 36 auto do_parse = [&]() -> Error { 37 FileSpec system_file_spec(module.system_path); 38 39 FileSpec local_file_spec(module.file.has_value() ? *module.file 40 : module.system_path); 41 42 ModuleSpec module_spec; 43 module_spec.GetFileSpec() = local_file_spec; 44 module_spec.GetPlatformFileSpec() = system_file_spec; 45 46 if (module.uuid.has_value()) 47 module_spec.GetUUID().SetFromStringRef(*module.uuid); 48 49 Status error; 50 ModuleSP module_sp = 51 target.GetOrCreateModule(module_spec, /*notify*/ false, &error); 52 53 if (error.Fail()) 54 return error.ToError(); 55 56 bool load_addr_changed = false; 57 module_sp->SetLoadAddress(target, module.load_address.value, false, 58 load_addr_changed); 59 return Error::success(); 60 }; 61 if (Error err = do_parse()) 62 return createStringError( 63 inconvertibleErrorCode(), "Error when parsing module %s. %s", 64 module.system_path.c_str(), toString(std::move(err)).c_str()); 65 return Error::success(); 66 } 67 68 Error TraceIntelPTBundleLoader::CreateJSONError(json::Path::Root &root, 69 const json::Value &value) { 70 std::string err; 71 raw_string_ostream os(err); 72 root.printErrorContext(value, os); 73 return createStringError( 74 std::errc::invalid_argument, "%s\n\nContext:\n%s\n\nSchema:\n%s", 75 toString(root.getError()).c_str(), os.str().c_str(), GetSchema().data()); 76 } 77 78 ThreadPostMortemTraceSP 79 TraceIntelPTBundleLoader::ParseThread(Process &process, 80 const JSONThread &thread) { 81 lldb::tid_t tid = static_cast<lldb::tid_t>(thread.tid); 82 83 std::optional<FileSpec> trace_file; 84 if (thread.ipt_trace) 85 trace_file = FileSpec(*thread.ipt_trace); 86 87 ThreadPostMortemTraceSP thread_sp = 88 std::make_shared<ThreadPostMortemTrace>(process, tid, trace_file); 89 process.GetThreadList().AddThread(thread_sp); 90 return thread_sp; 91 } 92 93 Expected<TraceIntelPTBundleLoader::ParsedProcess> 94 TraceIntelPTBundleLoader::CreateEmptyProcess(lldb::pid_t pid, 95 llvm::StringRef triple) { 96 TargetSP target_sp; 97 Status error = m_debugger.GetTargetList().CreateTarget( 98 m_debugger, /*user_exe_path*/ StringRef(), triple, eLoadDependentsNo, 99 /*platform_options*/ nullptr, target_sp); 100 101 if (!target_sp) 102 return error.ToError(); 103 104 ParsedProcess parsed_process; 105 parsed_process.target_sp = target_sp; 106 107 ProcessTrace::Initialize(); 108 ProcessSP process_sp = target_sp->CreateProcess( 109 /*listener*/ nullptr, "trace", 110 /*crash_file*/ nullptr, 111 /*can_connect*/ false); 112 113 process_sp->SetID(static_cast<lldb::pid_t>(pid)); 114 115 return parsed_process; 116 } 117 118 Expected<TraceIntelPTBundleLoader::ParsedProcess> 119 TraceIntelPTBundleLoader::ParseProcess(const JSONProcess &process) { 120 Expected<ParsedProcess> parsed_process = 121 CreateEmptyProcess(process.pid, process.triple.value_or("")); 122 123 if (!parsed_process) 124 return parsed_process.takeError(); 125 126 ProcessSP process_sp = parsed_process->target_sp->GetProcessSP(); 127 128 for (const JSONThread &thread : process.threads) 129 parsed_process->threads.push_back(ParseThread(*process_sp, thread)); 130 131 for (const JSONModule &module : process.modules) 132 if (Error err = ParseModule(*parsed_process->target_sp, module)) 133 return std::move(err); 134 135 if (!process.threads.empty()) 136 process_sp->GetThreadList().SetSelectedThreadByIndexID(0); 137 138 // We invoke DidAttach to create a correct stopped state for the process and 139 // its threads. 140 ArchSpec process_arch; 141 process_sp->DidAttach(process_arch); 142 143 return parsed_process; 144 } 145 146 Expected<TraceIntelPTBundleLoader::ParsedProcess> 147 TraceIntelPTBundleLoader::ParseKernel( 148 const JSONTraceBundleDescription &bundle_description) { 149 Expected<ParsedProcess> parsed_process = 150 CreateEmptyProcess(kDefaultKernelProcessID, ""); 151 152 if (!parsed_process) 153 return parsed_process.takeError(); 154 155 ProcessSP process_sp = parsed_process->target_sp->GetProcessSP(); 156 157 // Add cpus as fake threads 158 for (const JSONCpu &cpu : *bundle_description.cpus) { 159 ThreadPostMortemTraceSP thread_sp = std::make_shared<ThreadPostMortemTrace>( 160 *process_sp, static_cast<lldb::tid_t>(cpu.id), FileSpec(cpu.ipt_trace)); 161 thread_sp->SetName(formatv("kernel_cpu_{0}", cpu.id).str().c_str()); 162 process_sp->GetThreadList().AddThread(thread_sp); 163 parsed_process->threads.push_back(thread_sp); 164 } 165 166 // Add kernel image 167 FileSpec file_spec(bundle_description.kernel->file); 168 ModuleSpec module_spec; 169 module_spec.GetFileSpec() = file_spec; 170 171 Status error; 172 ModuleSP module_sp = 173 parsed_process->target_sp->GetOrCreateModule(module_spec, false, &error); 174 175 if (error.Fail()) 176 return error.ToError(); 177 178 lldb::addr_t load_address = 179 bundle_description.kernel->load_address 180 ? bundle_description.kernel->load_address->value 181 : kDefaultKernelLoadAddress; 182 183 bool load_addr_changed = false; 184 module_sp->SetLoadAddress(*parsed_process->target_sp, load_address, false, 185 load_addr_changed); 186 187 process_sp->GetThreadList().SetSelectedThreadByIndexID(0); 188 189 // We invoke DidAttach to create a correct stopped state for the process and 190 // its threads. 191 ArchSpec process_arch; 192 process_sp->DidAttach(process_arch); 193 194 return parsed_process; 195 } 196 197 Expected<std::vector<TraceIntelPTBundleLoader::ParsedProcess>> 198 TraceIntelPTBundleLoader::LoadBundle( 199 const JSONTraceBundleDescription &bundle_description) { 200 std::vector<ParsedProcess> parsed_processes; 201 202 auto HandleError = [&](Error &&err) { 203 // Delete all targets that were created so far in case of failures 204 for (ParsedProcess &parsed_process : parsed_processes) 205 m_debugger.GetTargetList().DeleteTarget(parsed_process.target_sp); 206 return std::move(err); 207 }; 208 209 if (bundle_description.processes) { 210 for (const JSONProcess &process : *bundle_description.processes) { 211 if (Expected<ParsedProcess> parsed_process = ParseProcess(process)) 212 parsed_processes.push_back(std::move(*parsed_process)); 213 else 214 return HandleError(parsed_process.takeError()); 215 } 216 } 217 218 if (bundle_description.kernel) { 219 if (Expected<ParsedProcess> kernel_process = 220 ParseKernel(bundle_description)) 221 parsed_processes.push_back(std::move(*kernel_process)); 222 else 223 return HandleError(kernel_process.takeError()); 224 } 225 226 return parsed_processes; 227 } 228 229 StringRef TraceIntelPTBundleLoader::GetSchema() { 230 static std::string schema; 231 if (schema.empty()) { 232 schema = R"({ 233 "type": "intel-pt", 234 "cpuInfo": { 235 // CPU information gotten from, for example, /proc/cpuinfo. 236 237 "vendor": "GenuineIntel" | "unknown", 238 "family": integer, 239 "model": integer, 240 "stepping": integer 241 }, 242 "processes?": [ 243 { 244 "pid": integer, 245 "triple"?: string, 246 // Optional clang/llvm target triple. 247 // This must be provided if the trace will be created not using the 248 // CLI or on a machine other than where the target was traced. 249 "threads": [ 250 // A list of known threads for the given process. When context switch 251 // data is provided, LLDB will automatically create threads for the 252 // this process whenever it finds new threads when traversing the 253 // context switches, so passing values to this list in this case is 254 // optional. 255 { 256 "tid": integer, 257 "iptTrace"?: string 258 // Path to the raw Intel PT buffer file for this thread. 259 } 260 ], 261 "modules": [ 262 { 263 "systemPath": string, 264 // Original path of the module at runtime. 265 "file"?: string, 266 // Path to a copy of the file if not available at "systemPath". 267 "loadAddress": integer | string decimal | hex string, 268 // Lowest address of the sections of the module loaded on memory. 269 "uuid"?: string, 270 // Build UUID for the file for sanity checks. 271 } 272 ] 273 } 274 ], 275 "cpus"?: [ 276 { 277 "id": integer, 278 // Id of this CPU core. 279 "iptTrace": string, 280 // Path to the raw Intel PT buffer for this cpu core. 281 "contextSwitchTrace": string, 282 // Path to the raw perf_event_open context switch trace file for this cpu core. 283 // The perf_event must have been configured with PERF_SAMPLE_TID and 284 // PERF_SAMPLE_TIME, as well as sample_id_all = 1. 285 } 286 ], 287 "tscPerfZeroConversion"?: { 288 // Values used to convert between TSCs and nanoseconds. See the time_zero 289 // section in https://man7.org/linux/man-pages/man2/perf_event_open.2.html 290 // for information. 291 292 "timeMult": integer, 293 "timeShift": integer, 294 "timeZero": integer | string decimal | hex string, 295 }, 296 "kernel"?: { 297 "loadAddress"?: integer | string decimal | hex string, 298 // Kernel's image load address. Defaults to 0xffffffff81000000, which 299 // is a load address of x86 architecture if KASLR is not enabled. 300 "file": string, 301 // Path to the kernel image. 302 } 303 } 304 305 Notes: 306 307 - All paths are either absolute or relative to folder containing the bundle 308 description file. 309 - "cpus" is provided if and only if processes[].threads[].iptTrace is not provided. 310 - "tscPerfZeroConversion" must be provided if "cpus" is provided. 311 - If "kernel" is provided, then the "processes" section must be empty or not 312 passed at all, and the "cpus" section must be provided. This configuration 313 indicates that the kernel was traced and user processes weren't. Besides 314 that, the kernel is treated as a single process with one thread per CPU 315 core. This doesn't handle actual kernel threads, but instead treats 316 all the instructions executed by the kernel on each core as an 317 individual thread.})"; 318 } 319 return schema; 320 } 321 322 Error TraceIntelPTBundleLoader::AugmentThreadsFromContextSwitches( 323 JSONTraceBundleDescription &bundle_description) { 324 if (!bundle_description.cpus || !bundle_description.processes) 325 return Error::success(); 326 327 if (!bundle_description.tsc_perf_zero_conversion) 328 return createStringError(inconvertibleErrorCode(), 329 "TSC to nanos conversion values are needed when " 330 "context switch information is provided."); 331 332 DenseMap<lldb::pid_t, JSONProcess *> indexed_processes; 333 DenseMap<JSONProcess *, DenseSet<tid_t>> indexed_threads; 334 335 for (JSONProcess &process : *bundle_description.processes) { 336 indexed_processes[process.pid] = &process; 337 for (JSONThread &thread : process.threads) 338 indexed_threads[&process].insert(thread.tid); 339 } 340 341 auto on_thread_seen = [&](lldb::pid_t pid, tid_t tid) { 342 auto proc = indexed_processes.find(pid); 343 if (proc == indexed_processes.end()) 344 return; 345 if (indexed_threads[proc->second].count(tid)) 346 return; 347 indexed_threads[proc->second].insert(tid); 348 proc->second->threads.push_back({tid, /*ipt_trace=*/std::nullopt}); 349 }; 350 351 for (const JSONCpu &cpu : *bundle_description.cpus) { 352 Error err = Trace::OnDataFileRead( 353 FileSpec(cpu.context_switch_trace), 354 [&](ArrayRef<uint8_t> data) -> Error { 355 Expected<std::vector<ThreadContinuousExecution>> executions = 356 DecodePerfContextSwitchTrace( 357 data, cpu.id, *bundle_description.tsc_perf_zero_conversion); 358 if (!executions) 359 return executions.takeError(); 360 for (const ThreadContinuousExecution &execution : *executions) 361 on_thread_seen(execution.pid, execution.tid); 362 return Error::success(); 363 }); 364 if (err) 365 return err; 366 } 367 return Error::success(); 368 } 369 370 Expected<TraceSP> TraceIntelPTBundleLoader::CreateTraceIntelPTInstance( 371 JSONTraceBundleDescription &bundle_description, 372 std::vector<ParsedProcess> &parsed_processes) { 373 std::vector<ThreadPostMortemTraceSP> threads; 374 std::vector<ProcessSP> processes; 375 for (const ParsedProcess &parsed_process : parsed_processes) { 376 processes.push_back(parsed_process.target_sp->GetProcessSP()); 377 threads.insert(threads.end(), parsed_process.threads.begin(), 378 parsed_process.threads.end()); 379 } 380 381 TraceIntelPT::TraceMode trace_mode = bundle_description.kernel 382 ? TraceIntelPT::TraceMode::KernelMode 383 : TraceIntelPT::TraceMode::UserMode; 384 385 TraceSP trace_instance = TraceIntelPT::CreateInstanceForPostmortemTrace( 386 bundle_description, processes, threads, trace_mode); 387 for (const ParsedProcess &parsed_process : parsed_processes) 388 parsed_process.target_sp->SetTrace(trace_instance); 389 390 return trace_instance; 391 } 392 393 void TraceIntelPTBundleLoader::NormalizeAllPaths( 394 JSONTraceBundleDescription &bundle_description) { 395 if (bundle_description.processes) { 396 for (JSONProcess &process : *bundle_description.processes) { 397 for (JSONModule &module : process.modules) { 398 module.system_path = NormalizePath(module.system_path).GetPath(); 399 if (module.file) 400 module.file = NormalizePath(*module.file).GetPath(); 401 } 402 for (JSONThread &thread : process.threads) { 403 if (thread.ipt_trace) 404 thread.ipt_trace = NormalizePath(*thread.ipt_trace).GetPath(); 405 } 406 } 407 } 408 if (bundle_description.cpus) { 409 for (JSONCpu &cpu : *bundle_description.cpus) { 410 cpu.context_switch_trace = 411 NormalizePath(cpu.context_switch_trace).GetPath(); 412 cpu.ipt_trace = NormalizePath(cpu.ipt_trace).GetPath(); 413 } 414 } 415 if (bundle_description.kernel) { 416 bundle_description.kernel->file = 417 NormalizePath(bundle_description.kernel->file).GetPath(); 418 } 419 } 420 421 Expected<TraceSP> TraceIntelPTBundleLoader::Load() { 422 json::Path::Root root("traceBundle"); 423 JSONTraceBundleDescription bundle_description; 424 if (!fromJSON(m_bundle_description, bundle_description, root)) 425 return CreateJSONError(root, m_bundle_description); 426 427 NormalizeAllPaths(bundle_description); 428 429 if (Error err = AugmentThreadsFromContextSwitches(bundle_description)) 430 return std::move(err); 431 432 if (Expected<std::vector<ParsedProcess>> parsed_processes = 433 LoadBundle(bundle_description)) 434 return CreateTraceIntelPTInstance(bundle_description, *parsed_processes); 435 else 436 return parsed_processes.takeError(); 437 } 438