1 //===-- Decoder.cpp ---------------------------------------------*- C++ -*-===//
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 "Decoder.h"
10
11 // C/C++ Includes
12 #include <cinttypes>
13 #include <cstring>
14
15 #include "lldb/API/SBModule.h"
16 #include "lldb/API/SBProcess.h"
17 #include "lldb/API/SBThread.h"
18
19 using namespace ptdecoder_private;
20
21 // This function removes entries of all the processes/threads which were once
22 // registered in the class but are not alive anymore because they died or
23 // finished executing.
RemoveDeadProcessesAndThreads(lldb::SBProcess & sbprocess)24 void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) {
25 lldb::SBTarget sbtarget = sbprocess.GetTarget();
26 lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
27 uint32_t num_targets = sbdebugger.GetNumTargets();
28
29 auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin();
30 while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) {
31 bool process_found = false;
32 lldb::SBTarget target;
33 lldb::SBProcess process;
34 for (uint32_t i = 0; i < num_targets; i++) {
35 target = sbdebugger.GetTargetAtIndex(i);
36 process = target.GetProcess();
37 if (process.GetUniqueID() == itr_process->first) {
38 process_found = true;
39 break;
40 }
41 }
42
43 // Remove the process's entry if it was not found in SBDebugger
44 if (!process_found) {
45 itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
46 continue;
47 }
48
49 // If the state of the process is exited or detached then remove process's
50 // entry. If not then remove entry for all those registered threads of this
51 // process that are not alive anymore.
52 lldb::StateType state = process.GetState();
53 if ((state == lldb::StateType::eStateDetached) ||
54 (state == lldb::StateType::eStateExited))
55 itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
56 else {
57 auto itr_thread = itr_process->second.begin();
58 while (itr_thread != itr_process->second.end()) {
59 if (itr_thread->first == LLDB_INVALID_THREAD_ID) {
60 ++itr_thread;
61 continue;
62 }
63
64 lldb::SBThread thread = process.GetThreadByID(itr_thread->first);
65 if (!thread.IsValid())
66 itr_thread = itr_process->second.erase(itr_thread);
67 else
68 ++itr_thread;
69 }
70 ++itr_process;
71 }
72 }
73 }
74
StartProcessorTrace(lldb::SBProcess & sbprocess,lldb::SBTraceOptions & sbtraceoptions,lldb::SBError & sberror)75 void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess,
76 lldb::SBTraceOptions &sbtraceoptions,
77 lldb::SBError &sberror) {
78 sberror.Clear();
79 CheckDebuggerID(sbprocess, sberror);
80 if (!sberror.Success())
81 return;
82
83 std::lock_guard<std::mutex> guard(
84 m_mapProcessUID_mapThreadID_TraceInfo_mutex);
85 RemoveDeadProcessesAndThreads(sbprocess);
86
87 if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
88 sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to "
89 "eTraceTypeProcessorTrace; ProcessID = "
90 "%" PRIu64,
91 sbprocess.GetProcessID());
92 return;
93 }
94 lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror);
95 if (!sberror.Success())
96 return;
97
98 const char *trace_tech_key = "trace-tech";
99 std::string trace_tech_value("intel-pt");
100 lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key);
101 if (!value.IsValid()) {
102 sberror.SetErrorStringWithFormat(
103 "key \"%s\" not set in custom trace parameters", trace_tech_key);
104 return;
105 }
106
107 char string_value[9];
108 size_t bytes_written = value.GetStringValue(
109 string_value, sizeof(string_value) / sizeof(*string_value));
110 if (!bytes_written ||
111 (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) {
112 sberror.SetErrorStringWithFormat(
113 "key \"%s\" not set in custom trace parameters", trace_tech_key);
114 return;
115 }
116
117 std::size_t pos =
118 trace_tech_value.find((const char *)string_value, 0, bytes_written);
119 if ((pos == std::string::npos)) {
120 sberror.SetErrorStringWithFormat(
121 "key \"%s\" not set to \"%s\" in custom trace parameters",
122 trace_tech_key, trace_tech_value.c_str());
123 return;
124 }
125
126 // Start Tracing
127 lldb::SBError error;
128 uint32_t unique_id = sbprocess.GetUniqueID();
129 lldb::tid_t tid = sbtraceoptions.getThreadID();
130 lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error);
131 if (!error.Success()) {
132 if (tid == LLDB_INVALID_THREAD_ID)
133 sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
134 error.GetCString(),
135 sbprocess.GetProcessID());
136 else
137 sberror.SetErrorStringWithFormat(
138 "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
139 error.GetCString(), tid, sbprocess.GetProcessID());
140 return;
141 }
142
143 MapThreadID_TraceInfo &mapThreadID_TraceInfo =
144 m_mapProcessUID_mapThreadID_TraceInfo[unique_id];
145 ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
146 trace_info.SetUniqueTraceInstance(trace);
147 trace_info.SetStopID(sbprocess.GetStopID());
148 }
149
StopProcessorTrace(lldb::SBProcess & sbprocess,lldb::SBError & sberror,lldb::tid_t tid)150 void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess,
151 lldb::SBError &sberror, lldb::tid_t tid) {
152 sberror.Clear();
153 CheckDebuggerID(sbprocess, sberror);
154 if (!sberror.Success()) {
155 return;
156 }
157
158 std::lock_guard<std::mutex> guard(
159 m_mapProcessUID_mapThreadID_TraceInfo_mutex);
160 RemoveDeadProcessesAndThreads(sbprocess);
161
162 uint32_t unique_id = sbprocess.GetUniqueID();
163 auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
164 if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
165 sberror.SetErrorStringWithFormat(
166 "tracing not active for this process; ProcessID = %" PRIu64,
167 sbprocess.GetProcessID());
168 return;
169 }
170
171 lldb::SBError error;
172 if (tid == LLDB_INVALID_THREAD_ID) {
173 // This implies to stop tracing on the whole process
174 lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID;
175 auto itr_thread = itr_process->second.begin();
176 while (itr_thread != itr_process->second.end()) {
177 // In the case when user started trace on the entire process and then
178 // registered newly spawned threads of this process in the class later,
179 // these newly spawned threads will have same trace id. If we stopped
180 // trace on the entire process then tracing stops automatically for these
181 // newly spawned registered threads. Stopping trace on them again will
182 // return error and therefore we need to skip stopping trace on them
183 // again.
184 lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
185 lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID();
186 if (lldb_pt_user_id != id_to_be_ignored) {
187 trace.StopTrace(error, itr_thread->first);
188 if (!error.Success()) {
189 std::string error_string(error.GetCString());
190 if ((error_string.find("tracing not active for this process") ==
191 std::string::npos) &&
192 (error_string.find("tracing not active for this thread") ==
193 std::string::npos)) {
194 sberror.SetErrorStringWithFormat(
195 "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
196 error_string.c_str(), itr_thread->first,
197 sbprocess.GetProcessID());
198 return;
199 }
200 }
201
202 if (itr_thread->first == LLDB_INVALID_THREAD_ID)
203 id_to_be_ignored = lldb_pt_user_id;
204 }
205 itr_thread = itr_process->second.erase(itr_thread);
206 }
207 m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
208 } else {
209 // This implies to stop tracing on a single thread.
210 // if 'tid' is registered in the class then get the trace id and stop trace
211 // on it. If it is not then check if tracing was ever started on the entire
212 // process (because there is a possibility that trace is still running for
213 // 'tid' but it was not registered in the class because user had started
214 // trace on the whole process and 'tid' spawned later). In that case, get
215 // the trace id of the process trace instance and stop trace on this thread.
216 // If tracing was never started on the entire process then return error
217 // because there is no way tracing is active on 'tid'.
218 MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
219 lldb::SBTrace trace;
220 auto itr = mapThreadID_TraceInfo.find(tid);
221 if (itr != mapThreadID_TraceInfo.end()) {
222 trace = itr->second.GetUniqueTraceInstance();
223 } else {
224 auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
225 if (itr != mapThreadID_TraceInfo.end()) {
226 trace = itr->second.GetUniqueTraceInstance();
227 } else {
228 sberror.SetErrorStringWithFormat(
229 "tracing not active for this thread; thread id=%" PRIu64
230 ", ProcessID = %" PRIu64,
231 tid, sbprocess.GetProcessID());
232 return;
233 }
234 }
235
236 // Stop Tracing
237 trace.StopTrace(error, tid);
238 if (!error.Success()) {
239 std::string error_string(error.GetCString());
240 sberror.SetErrorStringWithFormat(
241 "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
242 error_string.c_str(), tid, sbprocess.GetProcessID());
243 if (error_string.find("tracing not active") == std::string::npos)
244 return;
245 }
246 // Delete the entry of 'tid' from this class (if any)
247 mapThreadID_TraceInfo.erase(tid);
248 }
249 }
250
ReadTraceDataAndImageInfo(lldb::SBProcess & sbprocess,lldb::tid_t tid,lldb::SBError & sberror,ThreadTraceInfo & threadTraceInfo)251 void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess,
252 lldb::tid_t tid, lldb::SBError &sberror,
253 ThreadTraceInfo &threadTraceInfo) {
254 // Allocate trace data buffer and parse cpu info for 'tid' if it is registered
255 // for the first time in class
256 lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance();
257 Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
258 lldb::SBError error;
259 if (pt_buffer.size() == 0) {
260 lldb::SBTraceOptions traceoptions;
261 traceoptions.setThreadID(tid);
262 trace.GetTraceConfig(traceoptions, error);
263 if (!error.Success()) {
264 sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
265 error.GetCString(),
266 sbprocess.GetProcessID());
267 return;
268 }
269 if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
270 sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
271 "for this thread; thread id=%" PRIu64
272 ", ProcessID = %" PRIu64,
273 tid, sbprocess.GetProcessID());
274 return;
275 }
276
277 threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize());
278 lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
279 if (!sberror.Success())
280 return;
281 CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
282 ParseCPUInfo(pt_cpu, sbstructdata, sberror);
283 if (!sberror.Success())
284 return;
285 }
286
287 // Call LLDB API to get raw trace data for this thread
288 size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(),
289 pt_buffer.size(), 0, tid);
290 if (!error.Success()) {
291 sberror.SetErrorStringWithFormat(
292 "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
293 error.GetCString(), tid, sbprocess.GetProcessID());
294 return;
295 }
296 std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0);
297
298 // Get information of all the modules of the inferior
299 lldb::SBTarget sbtarget = sbprocess.GetTarget();
300 ReadExecuteSectionInfos &readExecuteSectionInfos =
301 threadTraceInfo.GetReadExecuteSectionInfos();
302 GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror);
303 if (!sberror.Success())
304 return;
305 }
306
DecodeProcessorTrace(lldb::SBProcess & sbprocess,lldb::tid_t tid,lldb::SBError & sberror,ThreadTraceInfo & threadTraceInfo)307 void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
308 lldb::SBError &sberror,
309 ThreadTraceInfo &threadTraceInfo) {
310 // Initialize instruction decoder
311 struct pt_insn_decoder *decoder = nullptr;
312 struct pt_config config;
313 Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
314 CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
315 ReadExecuteSectionInfos &readExecuteSectionInfos =
316 threadTraceInfo.GetReadExecuteSectionInfos();
317
318 InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer,
319 readExecuteSectionInfos, sberror);
320 if (!sberror.Success())
321 return;
322
323 // Start raw trace decoding
324 Instructions &instruction_list = threadTraceInfo.GetInstructionLog();
325 instruction_list.clear();
326 DecodeTrace(decoder, instruction_list, sberror);
327 }
328
329 // Raw trace decoding requires information of Read & Execute sections of each
330 // module of the inferior. This function updates internal state of the class to
331 // store this information.
GetTargetModulesInfo(lldb::SBTarget & sbtarget,ReadExecuteSectionInfos & readExecuteSectionInfos,lldb::SBError & sberror)332 void Decoder::GetTargetModulesInfo(
333 lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos,
334 lldb::SBError &sberror) {
335 if (!sbtarget.IsValid()) {
336 sberror.SetErrorStringWithFormat("Can't get target's modules info from "
337 "LLDB; process has an invalid target");
338 return;
339 }
340
341 lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable();
342 if (!target_file_spec.IsValid()) {
343 sberror.SetErrorStringWithFormat("Target has an invalid file spec");
344 return;
345 }
346
347 uint32_t num_modules = sbtarget.GetNumModules();
348 readExecuteSectionInfos.clear();
349
350 // Store information of all RX sections of each module of inferior
351 for (uint32_t i = 0; i < num_modules; i++) {
352 lldb::SBModule module = sbtarget.GetModuleAtIndex(i);
353 if (!module.IsValid()) {
354 sberror.SetErrorStringWithFormat(
355 "Can't get module info [ %" PRIu32
356 " ] of target \"%s\" from LLDB, invalid module",
357 i, target_file_spec.GetFilename());
358 return;
359 }
360
361 lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec();
362 if (!module_file_spec.IsValid()) {
363 sberror.SetErrorStringWithFormat(
364 "Can't get module info [ %" PRIu32
365 " ] of target \"%s\" from LLDB, invalid file spec",
366 i, target_file_spec.GetFilename());
367 return;
368 }
369
370 const char *image(module_file_spec.GetFilename());
371 lldb::SBError error;
372 char image_complete_path[1024];
373 uint32_t path_length = module_file_spec.GetPath(
374 image_complete_path, sizeof(image_complete_path));
375 size_t num_sections = module.GetNumSections();
376
377 // Store information of only RX sections
378 for (size_t idx = 0; idx < num_sections; idx++) {
379 lldb::SBSection section = module.GetSectionAtIndex(idx);
380 uint32_t section_permission = section.GetPermissions();
381 if ((section_permission & lldb::Permissions::ePermissionsReadable) &&
382 (section_permission & lldb::Permissions::ePermissionsExecutable)) {
383 lldb::SBData section_data = section.GetSectionData();
384 if (!section_data.IsValid()) {
385 sberror.SetErrorStringWithFormat(
386 "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
387 "\"%s\" from LLDB, invalid "
388 "data in \"%s\" section",
389 i, image, target_file_spec.GetFilename(), section.GetName());
390 return;
391 }
392
393 // In case section has no data, skip it.
394 if (section_data.GetByteSize() == 0)
395 continue;
396
397 if (!path_length) {
398 sberror.SetErrorStringWithFormat(
399 "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
400 "\"%s\" from LLDB, module "
401 "has an invalid path length",
402 i, image, target_file_spec.GetFilename());
403 return;
404 }
405
406 std::string image_path(image_complete_path, path_length);
407 readExecuteSectionInfos.emplace_back(
408 section.GetLoadAddress(sbtarget), section.GetFileOffset(),
409 section_data.GetByteSize(), image_path);
410 }
411 }
412 }
413 }
414
415 // Raw trace decoding requires information of the target cpu on which inferior
416 // is running. This function gets the Trace Configuration from LLDB, parses it
417 // for cpu model, family, stepping and vendor id info and updates the internal
418 // state of the class to store this information.
ParseCPUInfo(CPUInfo & pt_cpu,lldb::SBStructuredData & s,lldb::SBError & sberror)419 void Decoder::ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
420 lldb::SBError &sberror) {
421 lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt");
422 if (!custom_trace_params.IsValid()) {
423 sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo");
424 return;
425 }
426
427 uint64_t family = 0, model = 0, stepping = 0;
428 char vendor[32];
429 const char *key_family = "cpu_family";
430 const char *key_model = "cpu_model";
431 const char *key_stepping = "cpu_stepping";
432 const char *key_vendor = "cpu_vendor";
433
434 // parse family
435 lldb::SBStructuredData struct_family =
436 custom_trace_params.GetValueForKey(key_family);
437 if (!struct_family.IsValid()) {
438 sberror.SetErrorStringWithFormat(
439 "%s info missing in custom trace parameters", key_family);
440 return;
441 }
442 family = struct_family.GetIntegerValue(0x10000);
443 if (family > UINT16_MAX) {
444 sberror.SetErrorStringWithFormat(
445 "invalid CPU family value extracted from custom trace parameters");
446 return;
447 }
448 pt_cpu.family = (uint16_t)family;
449
450 // parse model
451 lldb::SBStructuredData struct_model =
452 custom_trace_params.GetValueForKey(key_model);
453 if (!struct_model.IsValid()) {
454 sberror.SetErrorStringWithFormat(
455 "%s info missing in custom trace parameters; family=%" PRIu16,
456 key_model, pt_cpu.family);
457 return;
458 }
459 model = struct_model.GetIntegerValue(0x100);
460 if (model > UINT8_MAX) {
461 sberror.SetErrorStringWithFormat("invalid CPU model value extracted from "
462 "custom trace parameters; family=%" PRIu16,
463 pt_cpu.family);
464 return;
465 }
466 pt_cpu.model = (uint8_t)model;
467
468 // parse stepping
469 lldb::SBStructuredData struct_stepping =
470 custom_trace_params.GetValueForKey(key_stepping);
471 if (!struct_stepping.IsValid()) {
472 sberror.SetErrorStringWithFormat(
473 "%s info missing in custom trace parameters; family=%" PRIu16
474 ", model=%" PRIu8,
475 key_stepping, pt_cpu.family, pt_cpu.model);
476 return;
477 }
478 stepping = struct_stepping.GetIntegerValue(0x100);
479 if (stepping > UINT8_MAX) {
480 sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted "
481 "from custom trace parameters; "
482 "family=%" PRIu16 ", model=%" PRIu8,
483 pt_cpu.family, pt_cpu.model);
484 return;
485 }
486 pt_cpu.stepping = (uint8_t)stepping;
487
488 // parse vendor info
489 pt_cpu.vendor = pcv_unknown;
490 lldb::SBStructuredData struct_vendor =
491 custom_trace_params.GetValueForKey(key_vendor);
492 if (!struct_vendor.IsValid()) {
493 sberror.SetErrorStringWithFormat(
494 "%s info missing in custom trace parameters; family=%" PRIu16
495 ", model=%" PRIu8 ", stepping=%" PRIu8,
496 key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping);
497 return;
498 }
499 auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor));
500 if (length && strstr(vendor, "GenuineIntel"))
501 pt_cpu.vendor = pcv_intel;
502 }
503
504 // Initialize trace decoder with pt_config structure and populate its image
505 // structure with inferior's memory image information. pt_config structure is
506 // initialized with trace buffer and cpu info of the inferior before storing it
507 // in trace decoder.
InitializePTInstDecoder(struct pt_insn_decoder ** decoder,struct pt_config * config,const CPUInfo & pt_cpu,Buffer & pt_buffer,const ReadExecuteSectionInfos & readExecuteSectionInfos,lldb::SBError & sberror) const508 void Decoder::InitializePTInstDecoder(
509 struct pt_insn_decoder **decoder, struct pt_config *config,
510 const CPUInfo &pt_cpu, Buffer &pt_buffer,
511 const ReadExecuteSectionInfos &readExecuteSectionInfos,
512 lldb::SBError &sberror) const {
513 if (!decoder || !config) {
514 sberror.SetErrorStringWithFormat("internal error");
515 return;
516 }
517
518 // Load cpu info of inferior's target in pt_config struct
519 pt_config_init(config);
520 config->cpu = pt_cpu;
521 int errcode = pt_cpu_errata(&(config->errata), &(config->cpu));
522 if (errcode < 0) {
523 sberror.SetErrorStringWithFormat("processor trace decoding library: "
524 "pt_cpu_errata() failed with error: "
525 "\"%s\"",
526 pt_errstr(pt_errcode(errcode)));
527 return;
528 }
529
530 // Load trace buffer's starting and end address in pt_config struct
531 config->begin = pt_buffer.data();
532 config->end = pt_buffer.data() + pt_buffer.size();
533
534 // Fill trace decoder with pt_config struct
535 *decoder = pt_insn_alloc_decoder(config);
536 if (*decoder == nullptr) {
537 sberror.SetErrorStringWithFormat("processor trace decoding library: "
538 "pt_insn_alloc_decoder() returned null "
539 "pointer");
540 return;
541 }
542
543 // Fill trace decoder's image with inferior's memory image information
544 struct pt_image *image = pt_insn_get_image(*decoder);
545 if (!image) {
546 sberror.SetErrorStringWithFormat("processor trace decoding library: "
547 "pt_insn_get_image() returned null "
548 "pointer");
549 pt_insn_free_decoder(*decoder);
550 return;
551 }
552
553 for (auto &itr : readExecuteSectionInfos) {
554 errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset,
555 itr.size, nullptr, itr.load_address);
556 if (errcode < 0) {
557 sberror.SetErrorStringWithFormat("processor trace decoding library: "
558 "pt_image_add_file() failed with error: "
559 "\"%s\"",
560 pt_errstr(pt_errcode(errcode)));
561 pt_insn_free_decoder(*decoder);
562 return;
563 }
564 }
565 }
566
AppendErrorWithOffsetToInstructionList(int errcode,uint64_t decoder_offset,Instructions & instruction_list,lldb::SBError & sberror)567 void Decoder::AppendErrorWithOffsetToInstructionList(
568 int errcode, uint64_t decoder_offset, Instructions &instruction_list,
569 lldb::SBError &sberror) {
570 sberror.SetErrorStringWithFormat(
571 "processor trace decoding library: \"%s\" [decoder_offset] => "
572 "[0x%" PRIu64 "]",
573 pt_errstr(pt_errcode(errcode)), decoder_offset);
574 instruction_list.emplace_back(sberror.GetCString());
575 }
576
AppendErrorWithoutOffsetToInstructionList(int errcode,Instructions & instruction_list,lldb::SBError & sberror)577 void Decoder::AppendErrorWithoutOffsetToInstructionList(
578 int errcode, Instructions &instruction_list, lldb::SBError &sberror) {
579 sberror.SetErrorStringWithFormat("processor trace decoding library: \"%s\"",
580 pt_errstr(pt_errcode(errcode)));
581 instruction_list.emplace_back(sberror.GetCString());
582 }
583
AppendErrorToInstructionList(int errcode,pt_insn_decoder * decoder,Instructions & instruction_list,lldb::SBError & sberror)584 int Decoder::AppendErrorToInstructionList(int errcode, pt_insn_decoder *decoder,
585 Instructions &instruction_list,
586 lldb::SBError &sberror) {
587 uint64_t decoder_offset = 0;
588 int errcode_off = pt_insn_get_offset(decoder, &decoder_offset);
589 if (errcode_off < 0) {
590 AppendErrorWithoutOffsetToInstructionList(errcode, instruction_list,
591 sberror);
592 return errcode_off;
593 }
594 AppendErrorWithOffsetToInstructionList(errcode, decoder_offset,
595 instruction_list, sberror);
596 return 0;
597 }
598
HandlePTInstructionEvents(pt_insn_decoder * decoder,int errcode,Instructions & instruction_list,lldb::SBError & sberror)599 int Decoder::HandlePTInstructionEvents(pt_insn_decoder *decoder, int errcode,
600 Instructions &instruction_list,
601 lldb::SBError &sberror) {
602 while (errcode & pts_event_pending) {
603 pt_event event;
604 errcode = pt_insn_event(decoder, &event, sizeof(event));
605 if (errcode < 0)
606 return errcode;
607
608 // The list of events are in
609 // https://github.com/intel/libipt/blob/master/doc/man/pt_qry_event.3.md
610 if (event.type == ptev_overflow) {
611 int append_errcode = AppendErrorToInstructionList(
612 errcode, decoder, instruction_list, sberror);
613 if (append_errcode < 0)
614 return append_errcode;
615 }
616 // Other events don't signal stream errors
617 }
618
619 return 0;
620 }
621
622 // Start actual decoding of raw trace
DecodeTrace(struct pt_insn_decoder * decoder,Instructions & instruction_list,lldb::SBError & sberror)623 void Decoder::DecodeTrace(struct pt_insn_decoder *decoder,
624 Instructions &instruction_list,
625 lldb::SBError &sberror) {
626 uint64_t decoder_offset = 0;
627
628 while (1) {
629 struct pt_insn insn;
630
631 // Try to sync the decoder. If it fails then get the decoder_offset and try
632 // to sync again. If the new_decoder_offset is same as decoder_offset then
633 // we will not succeed in syncing for any number of pt_insn_sync_forward()
634 // operations. Return in that case. Else keep resyncing until either end of
635 // trace stream is reached or pt_insn_sync_forward() passes.
636 int errcode = pt_insn_sync_forward(decoder);
637 if (errcode < 0) {
638 if (errcode == -pte_eos)
639 return;
640
641 int errcode_off = pt_insn_get_offset(decoder, &decoder_offset);
642 if (errcode_off < 0) {
643 AppendErrorWithoutOffsetToInstructionList(errcode, instruction_list,
644 sberror);
645 return;
646 }
647
648 sberror.SetErrorStringWithFormat(
649 "processor trace decoding library: \"%s\" [decoder_offset] => "
650 "[0x%" PRIu64 "]",
651 pt_errstr(pt_errcode(errcode)), decoder_offset);
652 instruction_list.emplace_back(sberror.GetCString());
653 while (1) {
654 errcode = pt_insn_sync_forward(decoder);
655 if (errcode >= 0)
656 break;
657
658 if (errcode == -pte_eos)
659 return;
660
661 uint64_t new_decoder_offset = 0;
662 errcode_off = pt_insn_get_offset(decoder, &new_decoder_offset);
663 if (errcode_off < 0) {
664 sberror.SetErrorStringWithFormat(
665 "processor trace decoding library: \"%s\"",
666 pt_errstr(pt_errcode(errcode)));
667 instruction_list.emplace_back(sberror.GetCString());
668 return;
669 } else if (new_decoder_offset <= decoder_offset) {
670 // We tried resyncing the decoder and decoder didn't make any
671 // progress because the offset didn't change. We will not make any
672 // progress further. Hence, returning in this situation.
673 return;
674 }
675 AppendErrorWithOffsetToInstructionList(errcode, new_decoder_offset,
676 instruction_list, sberror);
677 decoder_offset = new_decoder_offset;
678 }
679 }
680
681 while (1) {
682 errcode = HandlePTInstructionEvents(decoder, errcode, instruction_list,
683 sberror);
684 if (errcode < 0) {
685 int append_errcode = AppendErrorToInstructionList(
686 errcode, decoder, instruction_list, sberror);
687 if (append_errcode < 0)
688 return;
689 break;
690 }
691 errcode = pt_insn_next(decoder, &insn, sizeof(insn));
692 if (errcode < 0) {
693 if (insn.iclass == ptic_error)
694 break;
695
696 instruction_list.emplace_back(insn);
697
698 if (errcode == -pte_eos)
699 return;
700
701 Diagnose(decoder, errcode, sberror, &insn);
702 instruction_list.emplace_back(sberror.GetCString());
703 break;
704 }
705 instruction_list.emplace_back(insn);
706 if (errcode & pts_eos)
707 return;
708 }
709 }
710 }
711
712 // Function to diagnose and indicate errors during raw trace decoding
Diagnose(struct pt_insn_decoder * decoder,int decode_error,lldb::SBError & sberror,const struct pt_insn * insn)713 void Decoder::Diagnose(struct pt_insn_decoder *decoder, int decode_error,
714 lldb::SBError &sberror, const struct pt_insn *insn) {
715 int errcode;
716 uint64_t offset;
717
718 errcode = pt_insn_get_offset(decoder, &offset);
719 if (insn) {
720 if (errcode < 0)
721 sberror.SetErrorStringWithFormat(
722 "processor trace decoding library: \"%s\" [decoder_offset, "
723 "last_successful_decoded_ip] => [?, 0x%" PRIu64 "]",
724 pt_errstr(pt_errcode(decode_error)), insn->ip);
725 else
726 sberror.SetErrorStringWithFormat(
727 "processor trace decoding library: \"%s\" [decoder_offset, "
728 "last_successful_decoded_ip] => [0x%" PRIu64 ", 0x%" PRIu64 "]",
729 pt_errstr(pt_errcode(decode_error)), offset, insn->ip);
730 } else {
731 if (errcode < 0)
732 sberror.SetErrorStringWithFormat(
733 "processor trace decoding library: \"%s\"",
734 pt_errstr(pt_errcode(decode_error)));
735 else
736 sberror.SetErrorStringWithFormat(
737 "processor trace decoding library: \"%s\" [decoder_offset] => "
738 "[0x%" PRIu64 "]",
739 pt_errstr(pt_errcode(decode_error)), offset);
740 }
741 }
742
GetInstructionLogAtOffset(lldb::SBProcess & sbprocess,lldb::tid_t tid,uint32_t offset,uint32_t count,InstructionList & result_list,lldb::SBError & sberror)743 void Decoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess,
744 lldb::tid_t tid, uint32_t offset,
745 uint32_t count,
746 InstructionList &result_list,
747 lldb::SBError &sberror) {
748 sberror.Clear();
749 CheckDebuggerID(sbprocess, sberror);
750 if (!sberror.Success()) {
751 return;
752 }
753
754 std::lock_guard<std::mutex> guard(
755 m_mapProcessUID_mapThreadID_TraceInfo_mutex);
756 RemoveDeadProcessesAndThreads(sbprocess);
757
758 ThreadTraceInfo *threadTraceInfo = nullptr;
759 FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
760 if (!sberror.Success()) {
761 return;
762 }
763 if (threadTraceInfo == nullptr) {
764 sberror.SetErrorStringWithFormat("internal error");
765 return;
766 }
767
768 // Return instruction log by populating 'result_list'
769 Instructions &insn_list = threadTraceInfo->GetInstructionLog();
770 uint64_t sum = (uint64_t)offset + 1;
771 if (((insn_list.size() <= offset) && (count <= sum) &&
772 ((sum - count) >= insn_list.size())) ||
773 (count < 1)) {
774 sberror.SetErrorStringWithFormat(
775 "Instruction Log not available for offset=%" PRIu32
776 " and count=%" PRIu32 ", ProcessID = %" PRIu64,
777 offset, count, sbprocess.GetProcessID());
778 return;
779 }
780
781 Instructions::iterator itr_first =
782 (insn_list.size() <= offset) ? insn_list.begin()
783 : insn_list.begin() + insn_list.size() - sum;
784 Instructions::iterator itr_last =
785 (count <= sum) ? insn_list.begin() + insn_list.size() - (sum - count)
786 : insn_list.end();
787 Instructions::iterator itr = itr_first;
788 while (itr != itr_last) {
789 result_list.AppendInstruction(*itr);
790 ++itr;
791 }
792 }
793
GetProcessorTraceInfo(lldb::SBProcess & sbprocess,lldb::tid_t tid,TraceOptions & options,lldb::SBError & sberror)794 void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
795 TraceOptions &options,
796 lldb::SBError &sberror) {
797 sberror.Clear();
798 CheckDebuggerID(sbprocess, sberror);
799 if (!sberror.Success()) {
800 return;
801 }
802
803 std::lock_guard<std::mutex> guard(
804 m_mapProcessUID_mapThreadID_TraceInfo_mutex);
805 RemoveDeadProcessesAndThreads(sbprocess);
806
807 ThreadTraceInfo *threadTraceInfo = nullptr;
808 FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
809 if (!sberror.Success()) {
810 return;
811 }
812 if (threadTraceInfo == nullptr) {
813 sberror.SetErrorStringWithFormat("internal error");
814 return;
815 }
816
817 // Get SBTraceOptions from LLDB for 'tid', populate 'traceoptions' with it
818 lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance();
819 lldb::SBTraceOptions traceoptions;
820 lldb::SBError error;
821 traceoptions.setThreadID(tid);
822 trace.GetTraceConfig(traceoptions, error);
823 if (!error.Success()) {
824 std::string error_string(error.GetCString());
825 if (error_string.find("tracing not active") != std::string::npos) {
826 uint32_t unique_id = sbprocess.GetUniqueID();
827 auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
828 if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end())
829 return;
830 itr_process->second.erase(tid);
831 }
832 sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
833 error_string.c_str(),
834 sbprocess.GetProcessID());
835 return;
836 }
837 if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
838 sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
839 "for this thread; thread id=%" PRIu64
840 ", ProcessID = %" PRIu64,
841 tid, sbprocess.GetProcessID());
842 return;
843 }
844 options.setType(traceoptions.getType());
845 options.setTraceBufferSize(traceoptions.getTraceBufferSize());
846 options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize());
847 lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
848 if (!sberror.Success())
849 return;
850 options.setTraceParams(sbstructdata);
851 options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size());
852 }
853
FetchAndDecode(lldb::SBProcess & sbprocess,lldb::tid_t tid,lldb::SBError & sberror,ThreadTraceInfo ** threadTraceInfo)854 void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
855 lldb::SBError &sberror,
856 ThreadTraceInfo **threadTraceInfo) {
857 // Return with error if 'sbprocess' is not registered in the class
858 uint32_t unique_id = sbprocess.GetUniqueID();
859 auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
860 if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
861 sberror.SetErrorStringWithFormat(
862 "tracing not active for this process; ProcessID = %" PRIu64,
863 sbprocess.GetProcessID());
864 return;
865 }
866
867 if (tid == LLDB_INVALID_THREAD_ID) {
868 sberror.SetErrorStringWithFormat(
869 "invalid thread id provided; thread_id = %" PRIu64
870 ", ProcessID = %" PRIu64,
871 tid, sbprocess.GetProcessID());
872 return;
873 }
874
875 // Check whether 'tid' thread is registered in the class. If it is then in
876 // case StopID didn't change then return without doing anything (no need to
877 // read and decode trace data then). Otherwise, save new StopID and proceed
878 // with reading and decoding trace.
879 if (threadTraceInfo == nullptr) {
880 sberror.SetErrorStringWithFormat("internal error");
881 return;
882 }
883
884 MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
885 auto itr_thread = mapThreadID_TraceInfo.find(tid);
886 if (itr_thread != mapThreadID_TraceInfo.end()) {
887 if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) {
888 *threadTraceInfo = &(itr_thread->second);
889 return;
890 }
891 itr_thread->second.SetStopID(sbprocess.GetStopID());
892 } else {
893 // Implies 'tid' is not registered in the class. If tracing was never
894 // started on the entire process then return an error. Else try to register
895 // this thread and proceed with reading and decoding trace.
896 lldb::SBError error;
897 itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
898 if (itr_thread == mapThreadID_TraceInfo.end()) {
899 sberror.SetErrorStringWithFormat(
900 "tracing not active for this thread; ProcessID = %" PRIu64,
901 sbprocess.GetProcessID());
902 return;
903 }
904
905 lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
906 ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
907 trace_info.SetUniqueTraceInstance(trace);
908 trace_info.SetStopID(sbprocess.GetStopID());
909 itr_thread = mapThreadID_TraceInfo.find(tid);
910 }
911
912 // Get raw trace data and inferior image from LLDB for the registered thread
913 ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second);
914 if (!sberror.Success()) {
915 std::string error_string(sberror.GetCString());
916 if (error_string.find("tracing not active") != std::string::npos)
917 mapThreadID_TraceInfo.erase(itr_thread);
918 return;
919 }
920 // Decode raw trace data
921 DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second);
922 if (!sberror.Success()) {
923 return;
924 }
925 *threadTraceInfo = &(itr_thread->second);
926 }
927
928 // This function checks whether the provided SBProcess instance belongs to same
929 // SBDebugger with which this tool instance is associated.
CheckDebuggerID(lldb::SBProcess & sbprocess,lldb::SBError & sberror)930 void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess,
931 lldb::SBError &sberror) {
932 if (!sbprocess.IsValid()) {
933 sberror.SetErrorStringWithFormat("invalid process instance");
934 return;
935 }
936
937 lldb::SBTarget sbtarget = sbprocess.GetTarget();
938 if (!sbtarget.IsValid()) {
939 sberror.SetErrorStringWithFormat(
940 "process contains an invalid target; ProcessID = %" PRIu64,
941 sbprocess.GetProcessID());
942 return;
943 }
944
945 lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
946 if (!sbdebugger.IsValid()) {
947 sberror.SetErrorStringWithFormat("process's target contains an invalid "
948 "debugger instance; ProcessID = %" PRIu64,
949 sbprocess.GetProcessID());
950 return;
951 }
952
953 if (sbdebugger.GetID() != m_debugger_user_id) {
954 sberror.SetErrorStringWithFormat(
955 "process belongs to a different SBDebugger instance than the one for "
956 "which the tool is instantiated; ProcessID = %" PRIu64,
957 sbprocess.GetProcessID());
958 return;
959 }
960 }
961