Files
clang-p2996/lldb/tools/intel-features/intel-pt/Decoder.cpp
Chandler Carruth 2946cd7010 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

902 lines
34 KiB
C++

//===-- Decoder.cpp ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "Decoder.h"
// C/C++ Includes
#include <cinttypes>
#include <cstring>
#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;
}
}