forked from OSchip/llvm-project
326 lines
12 KiB
C++
326 lines
12 KiB
C++
//===-- Decoder.h -----------------------------------------------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef Decoder_h_
|
|
#define Decoder_h_
|
|
|
|
// C/C++ Includes
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "lldb/API/SBDebugger.h"
|
|
#include "lldb/API/SBError.h"
|
|
#include "lldb/API/SBProcess.h"
|
|
#include "lldb/API/SBStream.h"
|
|
#include "lldb/API/SBStructuredData.h"
|
|
#include "lldb/API/SBTarget.h"
|
|
#include "lldb/API/SBTrace.h"
|
|
#include "lldb/API/SBTraceOptions.h"
|
|
#include "lldb/lldb-enumerations.h"
|
|
#include "lldb/lldb-types.h"
|
|
|
|
#include "intel-pt.h"
|
|
|
|
namespace ptdecoder_private {
|
|
/// \class Instruction
|
|
/// Represents an assembly instruction containing raw
|
|
/// instruction bytes, instruction address along with information
|
|
/// regarding execution flow context and Intel(R) Processor Trace
|
|
/// context.
|
|
class Instruction {
|
|
public:
|
|
Instruction() : ip(0), data(), error(), iclass(ptic_error), speculative(0) {}
|
|
|
|
Instruction(const Instruction &insn) = default;
|
|
|
|
Instruction(const struct pt_insn &insn)
|
|
: ip(insn.ip), data(), error(insn.size == 0 ? "invalid instruction" : ""),
|
|
iclass(insn.iclass), speculative(insn.speculative) {
|
|
if (insn.size != 0)
|
|
data.assign(insn.raw, insn.raw + insn.size);
|
|
}
|
|
|
|
Instruction(const char *err)
|
|
: ip(0), data(), error(err ? err : "unknown error"), iclass(ptic_error),
|
|
speculative(0) {}
|
|
|
|
~Instruction() {}
|
|
|
|
uint64_t GetInsnAddress() const { return ip; }
|
|
|
|
size_t GetRawBytes(void *buf, size_t size) const {
|
|
if ((buf == nullptr) || (size == 0))
|
|
return data.size();
|
|
|
|
size_t bytes_to_read = ((size <= data.size()) ? size : data.size());
|
|
::memcpy(buf, data.data(), bytes_to_read);
|
|
return bytes_to_read;
|
|
}
|
|
|
|
const std::string &GetError() const { return error; }
|
|
|
|
bool GetSpeculative() const { return speculative; }
|
|
|
|
private:
|
|
uint64_t ip; // instruction address in inferior's memory image
|
|
std::vector<uint8_t> data; // raw bytes
|
|
std::string error; // Error string if instruction is invalid
|
|
enum pt_insn_class iclass; // classification of the instruction
|
|
// A collection of flags giving additional information about instruction
|
|
uint32_t speculative : 1; // Instruction was executed speculatively or not
|
|
};
|
|
|
|
/// \class InstructionList
|
|
/// Represents a list of assembly instructions. Each instruction is of
|
|
/// type Instruction.
|
|
class InstructionList {
|
|
public:
|
|
InstructionList() : m_insn_vec() {}
|
|
|
|
InstructionList(const InstructionList &insn_list)
|
|
: m_insn_vec(insn_list.m_insn_vec) {}
|
|
|
|
~InstructionList() {}
|
|
|
|
// Get number of instructions in the list
|
|
size_t GetSize() const { return m_insn_vec.size(); }
|
|
|
|
// Get instruction at index
|
|
Instruction GetInstructionAtIndex(uint32_t idx) {
|
|
return (idx < m_insn_vec.size() ? m_insn_vec[idx]
|
|
: Instruction("invalid instruction"));
|
|
}
|
|
|
|
// Append intruction at the end of the list
|
|
void AppendInstruction(Instruction inst) { m_insn_vec.push_back(inst); }
|
|
|
|
private:
|
|
std::vector<Instruction> m_insn_vec;
|
|
};
|
|
|
|
/// \class TraceOptions
|
|
/// Provides Intel(R) Processor Trace specific configuration options and
|
|
/// other information obtained by decoding and post-processing the trace
|
|
/// data. Currently, this information comprises of the total number of
|
|
/// assembly instructions executed for an inferior.
|
|
class TraceOptions : public lldb::SBTraceOptions {
|
|
public:
|
|
TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {}
|
|
|
|
~TraceOptions() {}
|
|
|
|
/// Get total number of assembly instructions obtained after decoding the
|
|
/// complete Intel(R) Processor Trace data obtained from LLDB.
|
|
///
|
|
/// \return
|
|
/// Total number of instructions.
|
|
uint32_t getInstructionLogSize() const { return m_insn_log_size; }
|
|
|
|
/// Set total number of assembly instructions.
|
|
///
|
|
/// \param[in] size
|
|
/// Value to be set.
|
|
void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; }
|
|
|
|
private:
|
|
uint32_t m_insn_log_size;
|
|
};
|
|
|
|
/// \class Decoder
|
|
/// This class makes use of Intel(R) Processor Trace hardware feature
|
|
/// (implememted inside LLDB) to gather trace data for an inferior (being
|
|
/// debugged with LLDB) to provide meaningful information out of it.
|
|
///
|
|
/// Currently the meaningful information comprises of the execution flow
|
|
/// of the inferior (in terms of assembly instructions executed). The class
|
|
/// enables user to:
|
|
/// - start the trace with configuration options for a thread/process,
|
|
/// - stop the trace for a thread/process,
|
|
/// - get the execution flow (assembly instructions) for a thread and
|
|
/// - get trace specific information for a thread
|
|
class Decoder {
|
|
public:
|
|
typedef std::vector<Instruction> Instructions;
|
|
|
|
Decoder(lldb::SBDebugger &sbdebugger)
|
|
: m_mapProcessUID_mapThreadID_TraceInfo_mutex(),
|
|
m_mapProcessUID_mapThreadID_TraceInfo(),
|
|
m_debugger_user_id(sbdebugger.GetID()) {}
|
|
|
|
~Decoder() {}
|
|
|
|
void StartProcessorTrace(lldb::SBProcess &sbprocess,
|
|
lldb::SBTraceOptions &sbtraceoptions,
|
|
lldb::SBError &sberror);
|
|
|
|
void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror,
|
|
lldb::tid_t tid = LLDB_INVALID_THREAD_ID);
|
|
|
|
void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid,
|
|
uint32_t offset, uint32_t count,
|
|
InstructionList &result_list,
|
|
lldb::SBError &sberror);
|
|
|
|
void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
|
|
TraceOptions &traceinfo, lldb::SBError &sberror);
|
|
|
|
private:
|
|
class ThreadTraceInfo;
|
|
typedef std::vector<uint8_t> Buffer;
|
|
|
|
// internal class to manage inferior's read-execute section information
|
|
class ReadExecuteSectionInfo {
|
|
public:
|
|
uint64_t load_address;
|
|
uint64_t file_offset;
|
|
uint64_t size;
|
|
std::string image_path;
|
|
|
|
ReadExecuteSectionInfo(const uint64_t addr, const uint64_t offset,
|
|
const uint64_t sz, const std::string &path)
|
|
: load_address(addr), file_offset(offset), size(sz), image_path(path) {}
|
|
|
|
ReadExecuteSectionInfo(const ReadExecuteSectionInfo &rxsection) = default;
|
|
};
|
|
|
|
typedef struct pt_cpu CPUInfo;
|
|
typedef std::vector<ReadExecuteSectionInfo> ReadExecuteSectionInfos;
|
|
|
|
// Check whether the provided SBProcess belongs to the same SBDebugger with
|
|
// which Decoder class instance was constructed.
|
|
void CheckDebuggerID(lldb::SBProcess &sbprocess, lldb::SBError &sberror);
|
|
|
|
// Function to remove entries of finished processes/threads in the class
|
|
void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess);
|
|
|
|
// Parse cpu information from trace configuration received from LLDB
|
|
void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s,
|
|
lldb::SBError &sberror);
|
|
|
|
/// Function performs following tasks for a given process and thread:
|
|
/// - Checks if the given thread is registered in the class or not. If not
|
|
/// then tries to register it if trace was ever started on the entire
|
|
/// process. Else returns error.
|
|
/// - fetches trace and other necessary information from LLDB (using
|
|
/// ReadTraceDataAndImageInfo()) and decodes the trace (using
|
|
/// DecodeProcessorTrace())
|
|
void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid,
|
|
lldb::SBError &sberror,
|
|
ThreadTraceInfo **threadTraceInfo);
|
|
|
|
// Helper function of FetchAndDecode() to get raw trace data and memory image
|
|
// info of inferior from LLDB
|
|
void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid,
|
|
lldb::SBError &sberror,
|
|
ThreadTraceInfo &threadTraceInfo);
|
|
|
|
// Helper function of FetchAndDecode() to initialize raw trace decoder and
|
|
// start trace decoding
|
|
void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid,
|
|
lldb::SBError &sberror,
|
|
ThreadTraceInfo &threadTraceInfo);
|
|
|
|
// Helper function of ReadTraceDataAndImageInfo() function for gathering
|
|
// inferior's memory image info along with all dynamic libraries linked with
|
|
// it
|
|
void GetTargetModulesInfo(lldb::SBTarget &sbtarget,
|
|
ReadExecuteSectionInfos &readExecuteSectionInfos,
|
|
lldb::SBError &sberror);
|
|
|
|
/// Helper functions of DecodeProcessorTrace() function for:
|
|
/// - initializing raw trace decoder (provided by Intel(R) Processor Trace
|
|
/// Decoding library)
|
|
/// - start trace decoding
|
|
void InitializePTInstDecoder(
|
|
struct pt_insn_decoder **decoder, struct pt_config *config,
|
|
const CPUInfo &pt_cpu, Buffer &pt_buffer,
|
|
const ReadExecuteSectionInfos &readExecuteSectionInfos,
|
|
lldb::SBError &sberror) const;
|
|
void DecodeTrace(struct pt_insn_decoder *decoder,
|
|
Instructions &instruction_list, lldb::SBError &sberror);
|
|
int HandlePTInstructionEvents(pt_insn_decoder *decoder, int errcode,
|
|
Instructions &instruction_list,
|
|
lldb::SBError &sberror);
|
|
|
|
int AppendErrorToInstructionList(int errcode, pt_insn_decoder *decoder,
|
|
Instructions &instruction_list,
|
|
lldb::SBError &sberror);
|
|
|
|
void AppendErrorWithOffsetToInstructionList(int errcode,
|
|
uint64_t decoder_offset,
|
|
Instructions &instruction_list,
|
|
lldb::SBError &sberror);
|
|
|
|
void AppendErrorWithoutOffsetToInstructionList(int errcode,
|
|
Instructions &instruction_list,
|
|
lldb::SBError &sberror);
|
|
|
|
// Function to diagnose and indicate errors during raw trace decoding
|
|
void Diagnose(struct pt_insn_decoder *decoder, int errcode,
|
|
lldb::SBError &sberror, const struct pt_insn *insn = nullptr);
|
|
|
|
class ThreadTraceInfo {
|
|
public:
|
|
ThreadTraceInfo()
|
|
: m_pt_buffer(), m_readExecuteSectionInfos(), m_thread_stop_id(0),
|
|
m_trace(), m_pt_cpu(), m_instruction_log() {}
|
|
|
|
ThreadTraceInfo(const ThreadTraceInfo &trace_info) = default;
|
|
|
|
~ThreadTraceInfo() {}
|
|
|
|
Buffer &GetPTBuffer() { return m_pt_buffer; }
|
|
|
|
void AllocatePTBuffer(uint64_t size) { m_pt_buffer.assign(size, 0); }
|
|
|
|
ReadExecuteSectionInfos &GetReadExecuteSectionInfos() {
|
|
return m_readExecuteSectionInfos;
|
|
}
|
|
|
|
CPUInfo &GetCPUInfo() { return m_pt_cpu; }
|
|
|
|
Instructions &GetInstructionLog() { return m_instruction_log; }
|
|
|
|
uint32_t GetStopID() const { return m_thread_stop_id; }
|
|
|
|
void SetStopID(uint32_t stop_id) { m_thread_stop_id = stop_id; }
|
|
|
|
lldb::SBTrace &GetUniqueTraceInstance() { return m_trace; }
|
|
|
|
void SetUniqueTraceInstance(lldb::SBTrace &trace) { m_trace = trace; }
|
|
|
|
friend class Decoder;
|
|
|
|
private:
|
|
Buffer m_pt_buffer; // raw trace buffer
|
|
ReadExecuteSectionInfos
|
|
m_readExecuteSectionInfos; // inferior's memory image info
|
|
uint32_t m_thread_stop_id; // stop id for thread
|
|
lldb::SBTrace m_trace; // unique tracing instance of a thread/process
|
|
CPUInfo m_pt_cpu; // cpu info of the target on which inferior is running
|
|
Instructions m_instruction_log; // complete instruction log
|
|
};
|
|
|
|
typedef std::map<lldb::user_id_t, ThreadTraceInfo> MapThreadID_TraceInfo;
|
|
typedef std::map<uint32_t, MapThreadID_TraceInfo>
|
|
MapProcessUID_MapThreadID_TraceInfo;
|
|
|
|
std::mutex m_mapProcessUID_mapThreadID_TraceInfo_mutex;
|
|
MapProcessUID_MapThreadID_TraceInfo
|
|
m_mapProcessUID_mapThreadID_TraceInfo; // to store trace information for
|
|
// each process and its associated
|
|
// threads
|
|
lldb::user_id_t m_debugger_user_id; // SBDebugger instance which is associated
|
|
// to this Decoder instance
|
|
};
|
|
|
|
} // namespace ptdecoder_private
|
|
#endif // Decoder_h_
|