diff options
Diffstat (limited to 'tools/intel-features/intel-pt/Decoder.cpp')
| -rw-r--r-- | tools/intel-features/intel-pt/Decoder.cpp | 904 |
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; + } +} |
