summaryrefslogtreecommitdiff
path: root/tools/intel-features/intel-pt/Decoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/intel-features/intel-pt/Decoder.cpp')
-rw-r--r--tools/intel-features/intel-pt/Decoder.cpp904
1 files changed, 904 insertions, 0 deletions
diff --git a/tools/intel-features/intel-pt/Decoder.cpp b/tools/intel-features/intel-pt/Decoder.cpp
new file mode 100644
index 000000000000..0c385adc811c
--- /dev/null
+++ b/tools/intel-features/intel-pt/Decoder.cpp
@@ -0,0 +1,904 @@
+//===-- Decoder.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// Project includes
+#include "Decoder.h"
+
+// C/C++ Includes
+#include <cinttypes>
+#include <cstring>
+
+// Other libraries and framework includes
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/API/SBThread.h"
+
+using namespace ptdecoder_private;
+
+// This function removes entries of all the processes/threads which were once
+// registered in the class but are not alive anymore because they died or
+// finished executing.
+void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) {
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
+ uint32_t num_targets = sbdebugger.GetNumTargets();
+
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin();
+ while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ bool process_found = false;
+ lldb::SBTarget target;
+ lldb::SBProcess process;
+ for (uint32_t i = 0; i < num_targets; i++) {
+ target = sbdebugger.GetTargetAtIndex(i);
+ process = target.GetProcess();
+ if (process.GetUniqueID() == itr_process->first) {
+ process_found = true;
+ break;
+ }
+ }
+
+ // Remove the process's entry if it was not found in SBDebugger
+ if (!process_found) {
+ itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ continue;
+ }
+
+ // If the state of the process is exited or detached then remove process's
+ // entry. If not then remove entry for all those registered threads of this
+ // process that are not alive anymore.
+ lldb::StateType state = process.GetState();
+ if ((state == lldb::StateType::eStateDetached) ||
+ (state == lldb::StateType::eStateExited))
+ itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ else {
+ auto itr_thread = itr_process->second.begin();
+ while (itr_thread != itr_process->second.end()) {
+ if (itr_thread->first == LLDB_INVALID_THREAD_ID) {
+ ++itr_thread;
+ continue;
+ }
+
+ lldb::SBThread thread = process.GetThreadByID(itr_thread->first);
+ if (!thread.IsValid())
+ itr_thread = itr_process->second.erase(itr_thread);
+ else
+ ++itr_thread;
+ }
+ ++itr_process;
+ }
+ }
+}
+
+void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBTraceOptions &sbtraceoptions,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success())
+ return;
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to "
+ "eTraceTypeProcessorTrace; ProcessID = "
+ "%" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+ lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+
+ const char *trace_tech_key = "trace-tech";
+ std::string trace_tech_value("intel-pt");
+ lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key);
+ if (!value.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set in custom trace parameters", trace_tech_key);
+ return;
+ }
+
+ char string_value[9];
+ size_t bytes_written = value.GetStringValue(
+ string_value, sizeof(string_value) / sizeof(*string_value));
+ if (!bytes_written ||
+ (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set in custom trace parameters", trace_tech_key);
+ return;
+ }
+
+ std::size_t pos =
+ trace_tech_value.find((const char *)string_value, 0, bytes_written);
+ if ((pos == std::string::npos)) {
+ sberror.SetErrorStringWithFormat(
+ "key \"%s\" not set to \"%s\" in custom trace parameters",
+ trace_tech_key, trace_tech_value.c_str());
+ return;
+ }
+
+ // Start Tracing
+ lldb::SBError error;
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ lldb::tid_t tid = sbtraceoptions.getThreadID();
+ lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error);
+ if (!error.Success()) {
+ if (tid == LLDB_INVALID_THREAD_ID)
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error.GetCString(),
+ sbprocess.GetProcessID());
+ else
+ sberror.SetErrorStringWithFormat(
+ "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
+ error.GetCString(), tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo =
+ m_mapProcessUID_mapThreadID_TraceInfo[unique_id];
+ ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
+ trace_info.SetUniqueTraceInstance(trace);
+ trace_info.SetStopID(sbprocess.GetStopID());
+}
+
+void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror, lldb::tid_t tid) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this process; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBError error;
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ // This implies to stop tracing on the whole process
+ lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID;
+ auto itr_thread = itr_process->second.begin();
+ while (itr_thread != itr_process->second.end()) {
+ // In the case when user started trace on the entire process and then
+ // registered newly spawned threads of this process in the class later,
+ // these newly spawned threads will have same trace id. If we stopped
+ // trace on the entire process then tracing stops automatically for these
+ // newly spawned registered threads. Stopping trace on them again will
+ // return error and therefore we need to skip stopping trace on them
+ // again.
+ lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
+ lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID();
+ if (lldb_pt_user_id != id_to_be_ignored) {
+ trace.StopTrace(error, itr_thread->first);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ if ((error_string.find("tracing not active for this process") ==
+ std::string::npos) &&
+ (error_string.find("tracing not active for this thread") ==
+ std::string::npos)) {
+ sberror.SetErrorStringWithFormat(
+ "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
+ error_string.c_str(), itr_thread->first,
+ sbprocess.GetProcessID());
+ return;
+ }
+ }
+
+ if (itr_thread->first == LLDB_INVALID_THREAD_ID)
+ id_to_be_ignored = lldb_pt_user_id;
+ }
+ itr_thread = itr_process->second.erase(itr_thread);
+ }
+ m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process);
+ } else {
+ // This implies to stop tracing on a single thread.
+ // if 'tid' is registered in the class then get the trace id and stop trace
+ // on it. If it is not then check if tracing was ever started on the entire
+ // process (because there is a possibility that trace is still running for
+ // 'tid' but it was not registered in the class because user had started
+ // trace on the whole process and 'tid' spawned later). In that case, get
+ // the trace id of the process trace instance and stop trace on this thread.
+ // If tracing was never started on the entire process then return error
+ // because there is no way tracing is active on 'tid'.
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
+ lldb::SBTrace trace;
+ auto itr = mapThreadID_TraceInfo.find(tid);
+ if (itr != mapThreadID_TraceInfo.end()) {
+ trace = itr->second.GetUniqueTraceInstance();
+ } else {
+ auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
+ if (itr != mapThreadID_TraceInfo.end()) {
+ trace = itr->second.GetUniqueTraceInstance();
+ } else {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+ }
+
+ // Stop Tracing
+ trace.StopTrace(error, tid);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ sberror.SetErrorStringWithFormat(
+ "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64,
+ error_string.c_str(), tid, sbprocess.GetProcessID());
+ if (error_string.find("tracing not active") == std::string::npos)
+ return;
+ }
+ // Delete the entry of 'tid' from this class (if any)
+ mapThreadID_TraceInfo.erase(tid);
+ }
+}
+
+void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo) {
+ // Allocate trace data buffer and parse cpu info for 'tid' if it is registered
+ // for the first time in class
+ lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance();
+ Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
+ lldb::SBError error;
+ if (pt_buffer.size() == 0) {
+ lldb::SBTraceOptions traceoptions;
+ traceoptions.setThreadID(tid);
+ trace.GetTraceConfig(traceoptions, error);
+ if (!error.Success()) {
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error.GetCString(),
+ sbprocess.GetProcessID());
+ return;
+ }
+ if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
+ "for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize());
+ lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+ CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
+ ParseCPUInfo(pt_cpu, sbstructdata, sberror);
+ if (!sberror.Success())
+ return;
+ }
+
+ // Call LLDB API to get raw trace data for this thread
+ size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(),
+ pt_buffer.size(), 0, tid);
+ if (!error.Success()) {
+ sberror.SetErrorStringWithFormat(
+ "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64,
+ error.GetCString(), tid, sbprocess.GetProcessID());
+ return;
+ }
+ std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0);
+
+ // Get information of all the modules of the inferior
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ ReadExecuteSectionInfos &readExecuteSectionInfos =
+ threadTraceInfo.GetReadExecuteSectionInfos();
+ GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror);
+ if (!sberror.Success())
+ return;
+}
+
+void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo &threadTraceInfo) {
+ // Initialize instruction decoder
+ struct pt_insn_decoder *decoder = nullptr;
+ struct pt_config config;
+ Buffer &pt_buffer = threadTraceInfo.GetPTBuffer();
+ CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo();
+ ReadExecuteSectionInfos &readExecuteSectionInfos =
+ threadTraceInfo.GetReadExecuteSectionInfos();
+
+ InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer,
+ readExecuteSectionInfos, sberror);
+ if (!sberror.Success())
+ return;
+
+ // Start raw trace decoding
+ Instructions &instruction_list = threadTraceInfo.GetInstructionLog();
+ instruction_list.clear();
+ DecodeTrace(decoder, instruction_list, sberror);
+}
+
+// Raw trace decoding requires information of Read & Execute sections of each
+// module of the inferior. This function updates internal state of the class to
+// store this information.
+void Decoder::GetTargetModulesInfo(
+ lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror) {
+ if (!sbtarget.IsValid()) {
+ sberror.SetErrorStringWithFormat("Can't get target's modules info from "
+ "LLDB; process has an invalid target");
+ return;
+ }
+
+ lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable();
+ if (!target_file_spec.IsValid()) {
+ sberror.SetErrorStringWithFormat("Target has an invalid file spec");
+ return;
+ }
+
+ uint32_t num_modules = sbtarget.GetNumModules();
+ readExecuteSectionInfos.clear();
+
+ // Store information of all RX sections of each module of inferior
+ for (uint32_t i = 0; i < num_modules; i++) {
+ lldb::SBModule module = sbtarget.GetModuleAtIndex(i);
+ if (!module.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32
+ " ] of target \"%s\" from LLDB, invalid module",
+ i, target_file_spec.GetFilename());
+ return;
+ }
+
+ lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec();
+ if (!module_file_spec.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32
+ " ] of target \"%s\" from LLDB, invalid file spec",
+ i, target_file_spec.GetFilename());
+ return;
+ }
+
+ const char *image(module_file_spec.GetFilename());
+ lldb::SBError error;
+ char image_complete_path[1024];
+ uint32_t path_length = module_file_spec.GetPath(
+ image_complete_path, sizeof(image_complete_path));
+ size_t num_sections = module.GetNumSections();
+
+ // Store information of only RX sections
+ for (size_t idx = 0; idx < num_sections; idx++) {
+ lldb::SBSection section = module.GetSectionAtIndex(idx);
+ uint32_t section_permission = section.GetPermissions();
+ if ((section_permission & lldb::Permissions::ePermissionsReadable) &&
+ (section_permission & lldb::Permissions::ePermissionsExecutable)) {
+ lldb::SBData section_data = section.GetSectionData();
+ if (!section_data.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
+ "\"%s\" from LLDB, invalid "
+ "data in \"%s\" section",
+ i, image, target_file_spec.GetFilename(), section.GetName());
+ return;
+ }
+
+ // In case section has no data, skip it.
+ if (section_data.GetByteSize() == 0)
+ continue;
+
+ if (!path_length) {
+ sberror.SetErrorStringWithFormat(
+ "Can't get module info [ %" PRIu32 " ] \"%s\" of target "
+ "\"%s\" from LLDB, module "
+ "has an invalid path length",
+ i, image, target_file_spec.GetFilename());
+ return;
+ }
+
+ std::string image_path(image_complete_path, path_length);
+ readExecuteSectionInfos.emplace_back(
+ section.GetLoadAddress(sbtarget), section.GetFileOffset(),
+ section_data.GetByteSize(), image_path);
+ }
+ }
+ }
+}
+
+// Raw trace decoding requires information of the target cpu on which inferior
+// is running. This function gets the Trace Configuration from LLDB, parses it
+// for cpu model, family, stepping and vendor id info and updates the internal
+// state of the class to store this information.
+void Decoder::ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
+ lldb::SBError &sberror) {
+ lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt");
+ if (!custom_trace_params.IsValid()) {
+ sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo");
+ return;
+ }
+
+ uint64_t family = 0, model = 0, stepping = 0;
+ char vendor[32];
+ const char *key_family = "cpu_family";
+ const char *key_model = "cpu_model";
+ const char *key_stepping = "cpu_stepping";
+ const char *key_vendor = "cpu_vendor";
+
+ // parse family
+ lldb::SBStructuredData struct_family =
+ custom_trace_params.GetValueForKey(key_family);
+ if (!struct_family.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters", key_family);
+ return;
+ }
+ family = struct_family.GetIntegerValue(0x10000);
+ if (family > UINT16_MAX) {
+ sberror.SetErrorStringWithFormat(
+ "invalid CPU family value extracted from custom trace parameters");
+ return;
+ }
+ pt_cpu.family = (uint16_t)family;
+
+ // parse model
+ lldb::SBStructuredData struct_model =
+ custom_trace_params.GetValueForKey(key_model);
+ if (!struct_model.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16,
+ key_model, pt_cpu.family);
+ return;
+ }
+ model = struct_model.GetIntegerValue(0x100);
+ if (model > UINT8_MAX) {
+ sberror.SetErrorStringWithFormat("invalid CPU model value extracted from "
+ "custom trace parameters; family=%" PRIu16,
+ pt_cpu.family);
+ return;
+ }
+ pt_cpu.model = (uint8_t)model;
+
+ // parse stepping
+ lldb::SBStructuredData struct_stepping =
+ custom_trace_params.GetValueForKey(key_stepping);
+ if (!struct_stepping.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16
+ ", model=%" PRIu8,
+ key_stepping, pt_cpu.family, pt_cpu.model);
+ return;
+ }
+ stepping = struct_stepping.GetIntegerValue(0x100);
+ if (stepping > UINT8_MAX) {
+ sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted "
+ "from custom trace parameters; "
+ "family=%" PRIu16 ", model=%" PRIu8,
+ pt_cpu.family, pt_cpu.model);
+ return;
+ }
+ pt_cpu.stepping = (uint8_t)stepping;
+
+ // parse vendor info
+ pt_cpu.vendor = pcv_unknown;
+ lldb::SBStructuredData struct_vendor =
+ custom_trace_params.GetValueForKey(key_vendor);
+ if (!struct_vendor.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "%s info missing in custom trace parameters; family=%" PRIu16
+ ", model=%" PRIu8 ", stepping=%" PRIu8,
+ key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping);
+ return;
+ }
+ auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor));
+ if (length && strstr(vendor, "GenuineIntel"))
+ pt_cpu.vendor = pcv_intel;
+}
+
+// Initialize trace decoder with pt_config structure and populate its image
+// structure with inferior's memory image information. pt_config structure is
+// initialized with trace buffer and cpu info of the inferior before storing it
+// in trace decoder.
+void Decoder::InitializePTInstDecoder(
+ struct pt_insn_decoder **decoder, struct pt_config *config,
+ const CPUInfo &pt_cpu, Buffer &pt_buffer,
+ const ReadExecuteSectionInfos &readExecuteSectionInfos,
+ lldb::SBError &sberror) const {
+ if (!decoder || !config) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Load cpu info of inferior's target in pt_config struct
+ pt_config_init(config);
+ config->cpu = pt_cpu;
+ int errcode = pt_cpu_errata(&(config->errata), &(config->cpu));
+ if (errcode < 0) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_cpu_errata() failed with error: "
+ "\"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ return;
+ }
+
+ // Load trace buffer's starting and end address in pt_config struct
+ config->begin = pt_buffer.data();
+ config->end = pt_buffer.data() + pt_buffer.size();
+
+ // Fill trace decoder with pt_config struct
+ *decoder = pt_insn_alloc_decoder(config);
+ if (*decoder == nullptr) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_insn_alloc_decoder() returned null "
+ "pointer");
+ return;
+ }
+
+ // Fill trace decoder's image with inferior's memory image information
+ struct pt_image *image = pt_insn_get_image(*decoder);
+ if (!image) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_insn_get_image() returned null "
+ "pointer");
+ pt_insn_free_decoder(*decoder);
+ return;
+ }
+
+ for (auto &itr : readExecuteSectionInfos) {
+ errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset,
+ itr.size, nullptr, itr.load_address);
+ if (errcode < 0) {
+ sberror.SetErrorStringWithFormat("processor trace decoding library: "
+ "pt_image_add_file() failed with error: "
+ "\"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ pt_insn_free_decoder(*decoder);
+ return;
+ }
+ }
+}
+
+// Start actual decoding of raw trace
+void Decoder::DecodeTrace(struct pt_insn_decoder *decoder,
+ Instructions &instruction_list,
+ lldb::SBError &sberror) {
+ uint64_t decoder_offset = 0;
+
+ while (1) {
+ struct pt_insn insn;
+
+ // Try to sync the decoder. If it fails then get the decoder_offset and try
+ // to sync again. If the new_decoder_offset is same as decoder_offset then
+ // we will not succeed in syncing for any number of pt_insn_sync_forward()
+ // operations. Return in that case. Else keep resyncing until either end of
+ // trace stream is reached or pt_insn_sync_forward() passes.
+ int errcode = pt_insn_sync_forward(decoder);
+ if (errcode < 0) {
+ if (errcode == -pte_eos)
+ return;
+
+ int errcode_off = pt_insn_get_offset(decoder, &decoder_offset);
+ if (errcode_off < 0) {
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ instruction_list.emplace_back(sberror.GetCString());
+ return;
+ }
+
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(errcode)), decoder_offset);
+ instruction_list.emplace_back(sberror.GetCString());
+ while (1) {
+ errcode = pt_insn_sync_forward(decoder);
+ if (errcode >= 0)
+ break;
+
+ if (errcode == -pte_eos)
+ return;
+
+ uint64_t new_decoder_offset = 0;
+ errcode_off = pt_insn_get_offset(decoder, &new_decoder_offset);
+ if (errcode_off < 0) {
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(errcode)));
+ instruction_list.emplace_back(sberror.GetCString());
+ return;
+ } else if (new_decoder_offset <= decoder_offset) {
+ // We tried resyncing the decoder and decoder didn't make any
+ // progress because the offset didn't change. We will not make any
+ // progress further. Hence, returning in this situation.
+ return;
+ }
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(errcode)), new_decoder_offset);
+ instruction_list.emplace_back(sberror.GetCString());
+ decoder_offset = new_decoder_offset;
+ }
+ }
+
+ while (1) {
+ errcode = pt_insn_next(decoder, &insn, sizeof(insn));
+ if (errcode < 0) {
+ if (insn.iclass == ptic_error)
+ break;
+
+ instruction_list.emplace_back(insn);
+
+ if (errcode == -pte_eos)
+ return;
+
+ Diagnose(decoder, errcode, sberror, &insn);
+ instruction_list.emplace_back(sberror.GetCString());
+ break;
+ }
+ instruction_list.emplace_back(insn);
+ if (errcode & pts_eos)
+ return;
+ }
+ }
+}
+
+// Function to diagnose and indicate errors during raw trace decoding
+void Decoder::Diagnose(struct pt_insn_decoder *decoder, int decode_error,
+ lldb::SBError &sberror, const struct pt_insn *insn) {
+ int errcode;
+ uint64_t offset;
+
+ errcode = pt_insn_get_offset(decoder, &offset);
+ if (insn) {
+ if (errcode < 0)
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset, "
+ "last_successful_decoded_ip] => [?, 0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), insn->ip);
+ else
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset, "
+ "last_successful_decoded_ip] => [0x%" PRIu64 ", 0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), offset, insn->ip);
+ } else {
+ if (errcode < 0)
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\"",
+ pt_errstr(pt_errcode(decode_error)));
+ else
+ sberror.SetErrorStringWithFormat(
+ "processor trace decoding library: \"%s\" [decoder_offset] => "
+ "[0x%" PRIu64 "]",
+ pt_errstr(pt_errcode(decode_error)), offset);
+ }
+}
+
+void Decoder::GetInstructionLogAtOffset(lldb::SBProcess &sbprocess,
+ lldb::tid_t tid, uint32_t offset,
+ uint32_t count,
+ InstructionList &result_list,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ ThreadTraceInfo *threadTraceInfo = nullptr;
+ FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
+ if (!sberror.Success()) {
+ return;
+ }
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Return instruction log by populating 'result_list'
+ Instructions &insn_list = threadTraceInfo->GetInstructionLog();
+ uint64_t sum = (uint64_t)offset + 1;
+ if (((insn_list.size() <= offset) && (count <= sum) &&
+ ((sum - count) >= insn_list.size())) ||
+ (count < 1)) {
+ sberror.SetErrorStringWithFormat(
+ "Instruction Log not available for offset=%" PRIu32
+ " and count=%" PRIu32 ", ProcessID = %" PRIu64,
+ offset, count, sbprocess.GetProcessID());
+ return;
+ }
+
+ Instructions::iterator itr_first =
+ (insn_list.size() <= offset) ? insn_list.begin()
+ : insn_list.begin() + insn_list.size() - sum;
+ Instructions::iterator itr_last =
+ (count <= sum) ? insn_list.begin() + insn_list.size() - (sum - count)
+ : insn_list.end();
+ Instructions::iterator itr = itr_first;
+ while (itr != itr_last) {
+ result_list.AppendInstruction(*itr);
+ ++itr;
+ }
+}
+
+void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ TraceOptions &options,
+ lldb::SBError &sberror) {
+ sberror.Clear();
+ CheckDebuggerID(sbprocess, sberror);
+ if (!sberror.Success()) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> guard(
+ m_mapProcessUID_mapThreadID_TraceInfo_mutex);
+ RemoveDeadProcessesAndThreads(sbprocess);
+
+ ThreadTraceInfo *threadTraceInfo = nullptr;
+ FetchAndDecode(sbprocess, tid, sberror, &threadTraceInfo);
+ if (!sberror.Success()) {
+ return;
+ }
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ // Get SBTraceOptions from LLDB for 'tid', populate 'traceoptions' with it
+ lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance();
+ lldb::SBTraceOptions traceoptions;
+ lldb::SBError error;
+ traceoptions.setThreadID(tid);
+ trace.GetTraceConfig(traceoptions, error);
+ if (!error.Success()) {
+ std::string error_string(error.GetCString());
+ if (error_string.find("tracing not active") != std::string::npos) {
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end())
+ return;
+ itr_process->second.erase(tid);
+ }
+ sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64,
+ error_string.c_str(),
+ sbprocess.GetProcessID());
+ return;
+ }
+ if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) {
+ sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB "
+ "for this thread; thread id=%" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+ options.setType(traceoptions.getType());
+ options.setTraceBufferSize(traceoptions.getTraceBufferSize());
+ options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize());
+ lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror);
+ if (!sberror.Success())
+ return;
+ options.setTraceParams(sbstructdata);
+ options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size());
+}
+
+void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
+ lldb::SBError &sberror,
+ ThreadTraceInfo **threadTraceInfo) {
+ // Return with error if 'sbprocess' is not registered in the class
+ uint32_t unique_id = sbprocess.GetUniqueID();
+ auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id);
+ if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this process; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ if (tid == LLDB_INVALID_THREAD_ID) {
+ sberror.SetErrorStringWithFormat(
+ "invalid thread id provided; thread_id = %" PRIu64
+ ", ProcessID = %" PRIu64,
+ tid, sbprocess.GetProcessID());
+ return;
+ }
+
+ // Check whether 'tid' thread is registered in the class. If it is then in
+ // case StopID didn't change then return without doing anything (no need to
+ // read and decode trace data then). Otherwise, save new StopID and proceed
+ // with reading and decoding trace.
+ if (threadTraceInfo == nullptr) {
+ sberror.SetErrorStringWithFormat("internal error");
+ return;
+ }
+
+ MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second;
+ auto itr_thread = mapThreadID_TraceInfo.find(tid);
+ if (itr_thread != mapThreadID_TraceInfo.end()) {
+ if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) {
+ *threadTraceInfo = &(itr_thread->second);
+ return;
+ }
+ itr_thread->second.SetStopID(sbprocess.GetStopID());
+ } else {
+ // Implies 'tid' is not registered in the class. If tracing was never
+ // started on the entire process then return an error. Else try to register
+ // this thread and proceed with reading and decoding trace.
+ lldb::SBError error;
+ itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID);
+ if (itr_thread == mapThreadID_TraceInfo.end()) {
+ sberror.SetErrorStringWithFormat(
+ "tracing not active for this thread; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance();
+ ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid];
+ trace_info.SetUniqueTraceInstance(trace);
+ trace_info.SetStopID(sbprocess.GetStopID());
+ itr_thread = mapThreadID_TraceInfo.find(tid);
+ }
+
+ // Get raw trace data and inferior image from LLDB for the registered thread
+ ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second);
+ if (!sberror.Success()) {
+ std::string error_string(sberror.GetCString());
+ if (error_string.find("tracing not active") != std::string::npos)
+ mapThreadID_TraceInfo.erase(itr_thread);
+ return;
+ }
+ // Decode raw trace data
+ DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second);
+ if (!sberror.Success()) {
+ return;
+ }
+ *threadTraceInfo = &(itr_thread->second);
+}
+
+// This function checks whether the provided SBProcess instance belongs to same
+// SBDebugger with which this tool instance is associated.
+void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess,
+ lldb::SBError &sberror) {
+ if (!sbprocess.IsValid()) {
+ sberror.SetErrorStringWithFormat("invalid process instance");
+ return;
+ }
+
+ lldb::SBTarget sbtarget = sbprocess.GetTarget();
+ if (!sbtarget.IsValid()) {
+ sberror.SetErrorStringWithFormat(
+ "process contains an invalid target; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ lldb::SBDebugger sbdebugger = sbtarget.GetDebugger();
+ if (!sbdebugger.IsValid()) {
+ sberror.SetErrorStringWithFormat("process's target contains an invalid "
+ "debugger instance; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+
+ if (sbdebugger.GetID() != m_debugger_user_id) {
+ sberror.SetErrorStringWithFormat(
+ "process belongs to a different SBDebugger instance than the one for "
+ "which the tool is instantiated; ProcessID = %" PRIu64,
+ sbprocess.GetProcessID());
+ return;
+ }
+}