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