Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -5,6 +5,10 @@ add_subdirectory(argdumper) add_subdirectory(driver) add_subdirectory(lldb-mi) +option(LLDB_BUILD_INTEL_PT "Enable Building of Intel(R) Processor Trace Tool" OFF) +if (LLDB_BUILD_INTEL_PT) + add_subdirectory(intel-pt) +endif() if (LLDB_CAN_USE_LLDB_SERVER) add_subdirectory(lldb-server) endif() Index: tools/intel-pt/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-pt/CMakeLists.txt @@ -0,0 +1,79 @@ +if (NOT LLDB_DISABLE_PYTHON ) + set(LLDB_INTEL_PT_WRAP_PYTHON ${LLDB_BINARY_DIR}/tools/intel-pt/scripts/IntelPTWrapPython.cpp) + set_source_files_properties(${LLDB_INTEL_PT_WRAP_PYTHON} PROPERTIES GENERATED 1) + + if (CLANG_CL) + set_source_files_properties(${LLDB_INTEL_PT_WRAP_PYTHON} + PROPERTIES COMPILE_FLAGS -Wno-unused-function) + endif() + + if (LLVM_COMPILER_IS_GCC_COMPATIBLE AND + NOT "${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") + set_property(SOURCE ${LLDB_INTEL_PT_WRAP_PYTHON} + APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-sequence-point -Wno-cast-qual") + endif () + + include(../../cmake/LLDBDependencies.cmake) + add_subdirectory(scripts) +endif() + +if (NOT LIBIPT_INCLUDE_PATH) + message (FATAL_ERROR "libipt include path not provided") +endif() + +if (NOT EXISTS "${LIBIPT_INCLUDE_PATH}") + message (FATAL_ERROR "invalid libipt include path provided") +endif() +include_directories(${LIBIPT_INCLUDE_PATH}) + +if (NOT LIBIPT_LIBRARY_PATH) + find_library(LIBIPT_LIBRARY ipt) +else() + if (NOT EXISTS "${LIBIPT_LIBRARY_PATH}") + message (FATAL_ERROR "invalid libipt library path provided") + endif() + find_library(LIBIPT_LIBRARY ipt PATHS ${LIBIPT_LIBRARY_PATH}) +endif() + +if (NOT LIBIPT_LIBRARY) + message (FATAL_ERROR "libipt library not found") +endif() + +add_library(lldbIntelPT SHARED + PTDecoder.cpp + Decoder.cpp + cli-wrapper.cpp + ${LLDB_INTEL_PT_WRAP_PYTHON} + ) + +target_link_libraries(lldbIntelPT PUBLIC liblldb) +target_link_libraries(lldbIntelPT PUBLIC ${LIBIPT_LIBRARY}) + + +if (NOT LLDB_DISABLE_PYTHON) + target_link_libraries(lldbIntelPT PRIVATE + lldbPluginScriptInterpreterNone + lldbPluginScriptInterpreterPython + ) + + if (LLDB_LINKER_SUPPORTS_GROUPS) + target_link_libraries(lldbIntelPT PUBLIC + -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) + target_link_libraries(lldbIntelPT PUBLIC + -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) + else() + target_link_libraries(lldbIntelPT PUBLIC ${LLDB_USED_LIBS}) + target_link_libraries(lldbIntelPT PUBLIC ${CLANG_USED_LIBS}) + endif() + + if (LLDB_INTEL_PT_WRAP_PYTHON) + add_dependencies(lldbIntelPT intel-pt-swig_wrapper) + target_link_libraries(lldbIntelPT PRIVATE ${LLDB_SYSTEM_LIBS}) + endif() + + llvm_config(lldbIntelPT ${LLVM_LINK_COMPONENTS}) +endif () + + +install(TARGETS lldbIntelPT + LIBRARY DESTINATION bin) Index: tools/intel-pt/Decoder.h =================================================================== --- /dev/null +++ tools/intel-pt/Decoder.h @@ -0,0 +1,296 @@ +//===-- Decoder.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef Decoder_h_ +#define Decoder_h_ + +// C/C++ Includes +#include +#include +#include +#include + +// Project includes, Other libraries and framework includes +#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 +/// @brief 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; } + + const uint8_t *GetRawBytes() const { return data.data(); } + + uint8_t GetRawBytesSize() const { return data.size(); } + + 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 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 Info +/// @brief 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 Info : public lldb::SBTraceOptions { +public: + Info() : lldb::SBTraceOptions(), m_insn_log_size(0) {} + + virtual ~Info() {} + + //------------------------------------------------------------------ + /// 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 +/// @brief 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 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, std::vector> &result_list, + lldb::SBError &sberror); + + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + Info &traceinfo, lldb::SBError &sberror); + +private: + class ThreadTraceInfo; + typedef std::vector 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 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); + + // Decode error codes returned by LLDB SB APIs and return them as strings + void DecodeErrorCode(const lldb::SBError &sberror, std::string &result); + + // 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::SBStream &s, lldb::SBError &sberror); + void Parse(const std::string &trace_params, const std::string &key, + lldb::SBError &sberror, std::string &result); + + ///------------------------------------------------------------------------ + /// 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); + + // 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 MapThreadID_TraceInfo; + typedef std::map + 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_ Index: tools/intel-pt/Decoder.cpp =================================================================== --- /dev/null +++ tools/intel-pt/Decoder.cpp @@ -0,0 +1,1076 @@ +//===-- 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 +#include + +// Other libraries and framework includes +#include "../../source/Plugins/Process/Linux/NativeProcessLinux.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" + +using namespace ptdecoder_private; + +void Decoder::DecodeErrorCode(const lldb::SBError &sberror, + std::string &result) { + uint32_t error_code = sberror.GetError(); + switch (error_code) { + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + ThreadNotTraced: + result.assign("tracing not active for this thread"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + UIDNotTraced: + result.assign("tracing not active for this thread"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + ProcessNotBeingTraced: + result.assign("tracing not active for this process"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + ThreadAlreadyBeingTraced: + result.assign("tracing already active on this thread"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + ProcessAlreadyBeingTraced: + result.assign("tracing already active on this process"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + InvalidFile: + result.assign("invalid file"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + PerfEventNotSupported: + result.assign("perf event not supported"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + PerfEventSyscallFailed: + result.assign("perf event syscall Failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + MetaBufferAllocFailed: + result.assign("Meta buffer allocation failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + TraceBufferAllocFailed: + result.assign("Trace buffer allocation failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + TraceBufferDeAllocFailed: + result.assign("Trace buffer deallocation failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + MetaBufferDeAllocFailed: + result.assign("Meta buffer deallocation failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + PerfEventCloseFailed: + result.assign("perf event close failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + EmptyTraceBuffer: + result.assign("empty Trace buffer"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + EmptyMetaBuffer: + result.assign("empty Meta buffer"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + CPUInfoNotFound: + result.assign("cpu info not found"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + NullPointer: + result.assign("null pointer"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + AllocFailed: + result.assign("alloc failed"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode::InvalidUID: + result.assign("invalid uid"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + TraceNotSupported: + result.assign("trace not supported"); + break; + case lldb_private::process_linux::NativeProcessLinux::PTErrorCode:: + InvalidThread: + result.assign("invalid thread id"); + break; + default: + result.assign(std::string(sberror.GetCString()) + ": " + + std::to_string(error_code)); + break; + } +} + +// 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 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; + lldb::SBStream s; + sberror = sbstructdata.GetAsJSON(s); + if (!sberror.Success()) + return; + + const char *custom_trace_params = s.GetData(); + std::string trace_params(custom_trace_params); + std::string trace_tech_key("trace-tech"); + std::string trace_tech_value("intel-pt"); + + if (!custom_trace_params || (s.GetSize() == 0)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", + trace_tech_key.c_str()); + return; + } + std::size_t pos = trace_params.find(trace_tech_key); + if ((pos == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", + trace_tech_key.c_str()); + return; + } + pos = trace_params.find(trace_tech_value); + if ((pos == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set to \"%s\" in custom trace parameters", + trace_tech_key.c_str(), 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()) { + std::string error_string; + DecodeErrorCode(error, error_string); + if (tid == LLDB_INVALID_THREAD_ID) + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error_string.c_str(), + sbprocess.GetProcessID()); + else + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), 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 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()) { + uint32_t error_code = error.GetError(); + if ((error_code != lldb_private::process_linux::NativeProcessLinux:: + PTErrorCode::ProcessNotBeingTraced) && + (error_code != lldb_private::process_linux::NativeProcessLinux:: + PTErrorCode::ThreadNotTraced)) { + std::string error_string; + DecodeErrorCode(error, error_string); + 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; + DecodeErrorCode(error, error_string); + 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()) { + std::string error_string; + DecodeErrorCode(error, error_string); + 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; + } + + threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + lldb::SBStream s; + sberror = sbstructdata.GetAsJSON(s); + if (!sberror.Success()) + return; + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ParseCPUInfo(pt_cpu, s, 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()) { + std::string error_string; + DecodeErrorCode(error, error_string); + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), 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); + try { + readExecuteSectionInfos.emplace_back( + section.GetLoadAddress(sbtarget), section.GetFileOffset(), + section_data.GetByteSize(), image_path); + } catch (const std::bad_alloc &le) { + continue; + } + } + } + } +} + +// 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::SBStream &s, + lldb::SBError &sberror) { + const char *custom_trace_params = s.GetData(); + if (!custom_trace_params || (s.GetSize() == 0)) { + sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo"); + return; + } + + long int family = 0, model = 0, stepping = 0, vendor = 0; + std::string trace_params(custom_trace_params); + std::string result_string; + std::size_t end; + + // parse family + std::string key("\"cpu_family\""); + Parse(trace_params, key, sberror, result_string); + if (!sberror.Success()) + return; + family = std::stol(result_string, &end, 16); + if ((result_string[end] != '\0') || family < 0 || family > UINT16_MAX) { + sberror.SetErrorStringWithFormat( + "invalid CPU family value extracted from custom trace parameters"); + return; + } + pt_cpu.family = (uint16_t)family; + + // parse model + key.assign("\"cpu_model\""); + Parse(trace_params, key, sberror, result_string); + if (!sberror.Success()) + return; + model = std::stol(result_string, &end, 16); + if ((result_string[end] != '\0') || model < 0 || 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 + key.assign("\"cpu_stepping\""); + Parse(trace_params, key, sberror, result_string); + if (!sberror.Success()) + return; + stepping = std::stol(result_string, &end, 16); + if ((result_string[end] != '\0') || stepping < 0 || 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; + key.assign("\"cpu_vendor_intel\""); + Parse(trace_params, key, sberror, result_string); + if (!sberror.Success()) + return; + vendor = std::stol(result_string, &end, 16); + if (vendor == 1) + pt_cpu.vendor = pcv_intel; +} + +void Decoder::Parse(const std::string &trace_params, const std::string &key, + lldb::SBError &sberror, std::string &result) { + std::string key_value_pair_separator(","); + std::string key_value_separator(":"); + std::string endof_key_value_pairs("}"); + result.clear(); + + std::size_t pos_key = trace_params.find(key); + if ((pos_key == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters", key.c_str()); + return; + } + + std::size_t pos_key_value_separator = + trace_params.find_first_of(key_value_separator, pos_key); + if (pos_key_value_separator == std::string::npos) { + sberror.SetErrorStringWithFormat( + "\":\" missing from (%s:value) in custom trace parameters", + key.c_str()); + return; + } + + std::size_t value_start_index = + pos_key_value_separator + key_value_separator.length(); + std::size_t value_end_index = + trace_params.find_first_of(key_value_pair_separator, pos_key); + + if (value_end_index == std::string::npos) { + value_end_index = + trace_params.find_first_of(endof_key_value_pairs, pos_key); + if (value_end_index == std::string::npos) { + sberror.SetErrorStringWithFormat( + "\"}\" missing after (%s:value) pair in custom trace parameters", + key.c_str()); + return; + } + } + + std::size_t value_string_length = value_end_index - value_start_index; + if (value_string_length < 1) { + sberror.SetErrorStringWithFormat( + "%s value missing in custom trace parameters", key.c_str()); + return; + } + result.assign(trace_params.substr(value_start_index, value_string_length)); +} + +// 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))); + try { + instruction_list.emplace_back(sberror.GetCString()); + } catch (const std::bad_alloc &le) { + } + return; + } + + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\" [decoder_offset] => " + "[0x%" PRIu64 "]", + pt_errstr(pt_errcode(errcode)), decoder_offset); + try { + instruction_list.emplace_back(sberror.GetCString()); + } catch (const std::bad_alloc &le) { + return; + } + 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))); + try { + instruction_list.emplace_back(sberror.GetCString()); + } catch (const std::bad_alloc &le) { + } + 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); + try { + instruction_list.emplace_back(sberror.GetCString()); + } catch (const std::bad_alloc &le) { + } + decoder_offset = new_decoder_offset; + } + } + + while (1) { + errcode = pt_insn_next(decoder, &insn, sizeof(insn)); + if (errcode < 0) { + if (insn.iclass == ptic_error) + break; + + try { + instruction_list.emplace_back(insn); + } catch (const std::bad_alloc &le) { + return; + } + + if (errcode == -pte_eos) + return; + + Diagnose(decoder, errcode, sberror, &insn); + try { + instruction_list.emplace_back(sberror.GetCString()); + } catch (const std::bad_alloc &le) { + return; + } + break; + } + try { + instruction_list.emplace_back(insn); + } catch (const std::bad_alloc &le) { + return; + } + 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, std::vector> &result_list, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard 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) { + try { + result_list.push_back( + std::shared_ptr(new Instruction(*itr))); + } catch (const std::bad_alloc &le) { + return; + } + ++itr; + } +} + +void Decoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + Info &traceinfo, lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard 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 'traceinfo' 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; + DecodeErrorCode(error, error_string); + 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; + } + traceinfo.setType(traceoptions.getType()); + traceinfo.setTraceBufferSize(traceoptions.getTraceBufferSize()); + traceinfo.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + traceinfo.setTraceParams(sbstructdata); + traceinfo.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; + } +} Index: tools/intel-pt/PTDecoder.h =================================================================== --- /dev/null +++ tools/intel-pt/PTDecoder.h @@ -0,0 +1,264 @@ +//===-- PTDecoder.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef PTDecoder_h_ +#define PTDecoder_h_ + +// C/C++ Includes +#include + +// Project includes, Other libraries and framework includes +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTraceOptions.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-types.h" + +namespace ptdecoder_private { +class Instruction; +class Info; +class Decoder; +} // namespace ptdecoder_private + +namespace ptdecoder { +//---------------------------------------------------------------------- +/// @class PTInstruction +/// @brief Represents an assembly instruction containing raw +/// instruction bytes, instruction address along with information +/// regarding execution flow context and Intel(R) Processor Trace +/// context. +//---------------------------------------------------------------------- +class PTInstruction { +public: + PTInstruction(); + + PTInstruction(const PTInstruction &insn); + + PTInstruction(const std::shared_ptr &ptr); + + ~PTInstruction(); + + // Get instruction address in inferior's memory image + uint64_t GetInsnAddress() const; + + // Get raw bytes of the instruction + const uint8_t *GetRawBytes() const; + + // Get size of raw bytes of the instruction + uint8_t GetRawBytesSize() const; + + // Get error string if it represents an invalid instruction. For a valid + // instruction, an empty string is returned + std::string GetError() const; + + // Instruction was executed speculatively or not + bool GetSpeculative() const; + +private: + std::shared_ptr m_opaque_sp; +}; + +//---------------------------------------------------------------------- +/// @class PTInfo +/// @brief Provides configuration options like trace type, trace buffer size, +/// meta data buffer size along with other Intel(R) Processor Trace +/// specific options. +//---------------------------------------------------------------------- +class PTInfo { +public: + PTInfo(); + + PTInfo(const PTInfo &pt_info); + + ~PTInfo(); + + lldb::TraceType GetType() const; + + uint64_t GetTraceBufferSize() const; + + uint64_t GetMetaDataBufferSize() const; + + //------------------------------------------------------------------ + /// Get Intel(R) Processor Trace specific configuration options (apart from + /// trace buffer size, meta data buffer size and TraceType) formatted as json + /// text i.e. {"Name":Value,"Name":Value} pairs, where "Value" is a 64-bit + /// unsigned integer in hex format. For "Name", please refer to + /// SBProcess::StartTrace API description for setting SBTraceOptions. + /// + /// @return + /// A string formatted as json text {"Name":Value,"Name":Value} + //------------------------------------------------------------------ + lldb::SBStructuredData GetTraceParams(lldb::SBError &error); + +private: + friend class PTDecoder; + + void SetSP(const std::shared_ptr &ptr); + + std::shared_ptr m_opaque_sp; +}; + +//---------------------------------------------------------------------- +/// @class PTDecoder +/// @brief 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 PTDecoder { +public: + PTDecoder(lldb::SBDebugger &sbdebugger); + + PTDecoder(const PTDecoder &ptdecoder); + + ~PTDecoder(); + + //------------------------------------------------------------------ + /// Start Intel(R) Processor Trace on a thread or complete process with + /// Intel(R) Processor Trace specific configuration options + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] sbtraceoptions + /// Contains thread id information and configuration options: + /// + /// For tracing a single thread, provide a valid thread id. If sbprocess + /// doesn't contain this thread id, error will be returned. For tracing + /// complete process, set it to lldb::LLDB_INVALID_THREAD_ID + /// Configuration options comprises of: + /// a) trace buffer size, meta data buffer size, TraceType and + /// b) All other possible Intel(R) Processor Trace specific configuration + /// options (hereafter collectively referred as CUSTOM_OPTIONS), formatted + /// as json text i.e. {"Name":Value,"Name":Value,..} inside + /// sbtraceoptions, where "Value" should be a 64-bit unsigned integer in + /// hex format. For information regarding what all configuration options + /// are currently supported by LLDB and detailed information about + /// CUSTOM_OPTIONS usage, please refer to SBProcess::StartTrace() API + /// description. To know about all possible configuration options of + /// Intel(R) Processor Trace, please refer to Intel(R) 64 and IA-32 + /// Architectures Software Developer's Manual. + /// + /// TraceType should be set to lldb::TraceType::eTraceTypeProcessorTrace, + /// else error is returned. To find out any other requirement to start + /// tracing successfully, please refer to SBProcess::StartTrace() API + /// description. LLDB's current implementation of Intel(R) Processor Trace + /// feature may round off invalid values for configuration options. + /// Therefore, the configuration options with which the trace was actually + /// started, might be different to the ones with which trace was asked to + /// be started by user. The actual used configuration options can be + /// obtained from GetProcessorTraceInfo() API. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror); + + //------------------------------------------------------------------ + /// Stop Intel(R) Processor Trace on a thread or complete process. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// Case 1: To stop tracing a single thread, provide a valid thread id. If + /// sbprocess doesn't contain the thread tid, error will be returned. + /// Case 2: To stop tracing complete process, use + /// lldb::LLDB_INVALID_THREAD_ID. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void StopProcessorTrace(lldb::SBProcess &sbprocess, lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID); + + //------------------------------------------------------------------ + /// Get instruction log containing the execution flow for a thread of a + /// process in terms of assembly instructions executed. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// A valid thread id of the thread for which instruction log is desired. + /// If sbprocess doesn't contain the thread tid, error will be returned. + /// + /// @param[in] count + /// The number of instructions requested by the user to be returned from + /// the complete instruction log. Complete instruction log refers to all + /// the assembly instructions obtained after decoding the complete raw + /// trace data obtained from LLDB. The length of the complete instruction + /// log is dependent on the trace buffer size with which processor tracing + /// was started for this thread. + /// The number of instructions actually returned are dependent on 'count' + /// and 'offset' parameters of this API. + /// + /// @param[in] offset + /// The offset in the complete instruction log from where 'count' number + /// of instructions are requested by the user. offset is counted from the + /// end of of this complete instruction log (which means the last executed + /// instruction is at offset 0 (zero)). + /// + /// @param[out] result_list + /// Depending upon 'count' and 'offset' values, instructions will be + /// appended to the end of this list. + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + std::vector &result_list, + lldb::SBError &sberror); + + //------------------------------------------------------------------ + /// Get Intel(R) Processor Trace specific information for a thread of a + /// process. The information contains the actual configuration options with + /// which the trace was started for this thread. + /// + /// @param[in] sbprocess + /// A valid process on which this operation will be performed. An error is + /// returned in case of an invalid process. + /// + /// @param[in] tid + /// A valid thread id of the thread for which the trace specific + /// information is required. If sbprocess doesn't contain the thread tid, + /// an error will be returned. + /// + /// @param[out] pt_info + /// Contains actual configuration options (they may be different to the + /// ones with which tracing was asked to be started for this thread during + /// StartProcessorTrace() API call). + /// + /// @param[out] sberror + /// An error with the failure reason if API fails. Else success. + //------------------------------------------------------------------ + void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTInfo &pt_info, lldb::SBError &sberror); + +private: + std::shared_ptr m_opaque_sp; +}; + +} // namespace ptdecoder +#endif // PTDecoder_h_ Index: tools/intel-pt/PTDecoder.cpp =================================================================== --- /dev/null +++ tools/intel-pt/PTDecoder.cpp @@ -0,0 +1,150 @@ +//===-- PTDecoder.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +// Project includes +#include "Decoder.h" +#include "PTDecoder.h" + +using namespace ptdecoder; +using namespace ptdecoder_private; + +// PTInstruction class member functions definitions +PTInstruction::PTInstruction() : m_opaque_sp() {} + +PTInstruction::PTInstruction(const PTInstruction &insn) + : m_opaque_sp(insn.m_opaque_sp) {} + +PTInstruction::PTInstruction( + const std::shared_ptr &ptr) + : m_opaque_sp(ptr) {} + +PTInstruction::~PTInstruction() {} + +uint64_t PTInstruction::GetInsnAddress() const { + return (m_opaque_sp ? m_opaque_sp->GetInsnAddress() : 0); +} + +const uint8_t *PTInstruction::GetRawBytes() const { + return (m_opaque_sp ? m_opaque_sp->GetRawBytes() : nullptr); +} + +uint8_t PTInstruction::GetRawBytesSize() const { + return (m_opaque_sp ? m_opaque_sp->GetRawBytesSize() : 0); +} + +std::string PTInstruction::GetError() const { + return (m_opaque_sp ? m_opaque_sp->GetError() : "null pointer"); +} + +bool PTInstruction::GetSpeculative() const { + return (m_opaque_sp ? m_opaque_sp->GetSpeculative() : 0); +} + +// PTInfo class member functions definitions +PTInfo::PTInfo() : m_opaque_sp() {} + +PTInfo::PTInfo(const PTInfo &pt_info) : m_opaque_sp(pt_info.m_opaque_sp) {} + +PTInfo::~PTInfo() {} + +lldb::TraceType PTInfo::GetType() const { + return (m_opaque_sp ? m_opaque_sp->getType() + : lldb::TraceType::eTraceTypeNone); +} + +uint64_t PTInfo::GetTraceBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getTraceBufferSize() : 0); +} + +uint64_t PTInfo::GetMetaDataBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getMetaDataBufferSize() : 0); +} + +lldb::SBStructuredData PTInfo::GetTraceParams(lldb::SBError &error) { + if (!m_opaque_sp) + error.SetErrorString("null pointer"); + return (m_opaque_sp ? m_opaque_sp->getTraceParams(error) + : lldb::SBStructuredData()); +} + +void PTInfo::SetSP(const std::shared_ptr &ptr) { + m_opaque_sp = ptr; +} + +// PTDecoder class member functions definitions +PTDecoder::PTDecoder(lldb::SBDebugger &sbdebugger) + : m_opaque_sp(new ptdecoder_private::Decoder(sbdebugger)) {} + +PTDecoder::PTDecoder(const PTDecoder &ptdecoder) + : m_opaque_sp(ptdecoder.m_opaque_sp) {} + +PTDecoder::~PTDecoder() {} + +void PTDecoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StartProcessorTrace(sbprocess, sbtraceoptions, sberror); +} + +void PTDecoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + m_opaque_sp->StopProcessorTrace(sbprocess, sberror, tid); +} + +void PTDecoder::GetInstructionLogAtOffset( + lldb::SBProcess &sbprocess, lldb::tid_t tid, uint32_t offset, + uint32_t count, std::vector &result_list, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::vector> vec_insn_ptr; + m_opaque_sp->GetInstructionLogAtOffset(sbprocess, tid, offset, count, + vec_insn_ptr, sberror); + if (!sberror.Success()) + return; + + for (auto &i : vec_insn_ptr) { + try { + result_list.emplace_back( + std::shared_ptr(i)); + } catch (const std::bad_alloc &le) { + return; + } + } +} + +void PTDecoder::GetProcessorTraceInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, PTInfo &pt_info, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr info_ptr(new Info()); + m_opaque_sp->GetProcessorTraceInfo(sbprocess, tid, *info_ptr, sberror); + if (!sberror.Success()) + return; + + pt_info.SetSP(info_ptr); +} Index: tools/intel-pt/README_CLI.txt =================================================================== --- /dev/null +++ tools/intel-pt/README_CLI.txt @@ -0,0 +1,124 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding 4 new CLI commands that * +* enable using Intel(R) Processor Trace Tool from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A C++ based cli wrapper has been developed to use Intel(R) Processor Trace Tool +through LLDB's command line. This also provides an idea to all developers on how +to integrate the Tool into various IDEs providing LLDB as a debugger. + + + +============ +How to Build +============ +The wrapper cli-wrapper.cpp needs to be compiled and linked with the shared +library of the Intel(R) Processor Trace Tool in order to be used through LLDB's +CLI. By default, this wrapper is built and linked with shared library of the +Tool to generate a new shared library if "How to Build" instructions mentioned +in README_TOOL.txt are followed. + + + +============ +How to Use +============ +All CLI commands provided by this wrapper can be used through the LLDB's CLI by +executing "plugin load " on LLDB CLI. shared_lib_name here +is lldbIntelPT.so + + + +============ +Description +============ +4 CLI commands have been designed keeping the LLDB's existing CLI command syntax +in mind. + + 1) processor-trace start [-b ] [] + + Start Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace start + + cmd-options Usage: + processor-trace start [-b ] [] + + -b + size of the trace buffer to store the trace data. If not specified + then a default value (=4KB) will be taken + + + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to start + tracing the whole process + + + + 2) processor-trace stop [] + + Stop Intel(R) Processor Trace on a specific thread or on the whole process + + Syntax: processor-trace stop + + cmd-options Usage: + processor-trace stop [] + + + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to stop + tracing the whole process + + + + 3) processor-trace show-info [] + + Display all the information regarding Intel(R) Processor Trace for a specific + thread or for the whole process. The information contains trace buffer + size and configuration options of Intel(R) Processor Trace. + + Syntax: processor-trace show-info + + cmd-options Usage: + processor-trace show-info [] + + + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to display + information for all threads of the process + + + + 4) processor-trace show-instr-log [-o ] [-c ] [] + + Display a log of assembly instructions executed for a specific thread or + for the whole process. The length of the log to be displayed and the + offset in the whole instruction log from where the log needs to be + displayed can also be provided. The offset is counted from the end of this + whole instruction log which means the last executed instruction is at + offset 0 (zero). + + Syntax: processor-trace show-instr-log + + cmd-options Usage: + processor-trace show-instr-log [-o ] [-c ] [] + + -c + number of instructions to be displayed. If not specified then a + default value (=10) will be taken + + -o + offset in the whole instruction log from where the log will be + displayed. If not specified then default value is calculated as + offset = count -1 + + + thread index of the thread. If no threads are specified, currently + selected thread is taken. Use the thread-index 'all' to show + instruction log for all the threads of the process Index: tools/intel-pt/README_TOOL.txt =================================================================== --- /dev/null +++ tools/intel-pt/README_TOOL.txt @@ -0,0 +1,309 @@ +******************************************************************************* +* README * +* * +* This file provides all the information regarding Intel(R) Processor Trace * +* Tool. It consists explanation about how Tool internally works, its hardware * +* and software dependencies, build procedure and usage of the API. * +******************************************************************************* + + + +============ +Introduction +============ +The Intel(R) Processor Trace Tool is developed on top of LLDB and provides its +its users execution trace of the debugged applications. Tool makes use of +Intel(R) Processor Trace hardware feature implementation inside LLDB for this +purpose. This hardware feature generates a set of trace packets that +encapsulates program flow information. These trace packets along with the binary +of the application can be decoded with the help of a software decoder to +construct the execution trace of the application. + +More information about Intel(R) Processor Trace feature can be obtained from +website: https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing + + + + +========= +Details +========= +The functionality of the Tool consists three parts: + +1. Raw Trace Collection from LLDB + With the help of API of this Tool (given below), Intel(R) Processor Trace + can be started on the application being debugged with LLDB. The generated + trace of the application is gathered inside LLDB and is collected by the + Tool from LLDB through LLDB's public API. + +2. Raw Trace Decoding + For decoding the raw trace data, the Tool makes use of "libipt", an + Intel(R) Processor Trace Decoder Library. The library needs binary of + the application and information about the cpu on which the application is + running in order to decode the raw trace. The Tool gathers this + information from LLDB public API and provide it to "libipt". More + information about "libipt" can be found at: + https://software.intel.com/en-us/blogs/2013/09/18/processor-tracing and + https://github.com/01org/processor-trace + +3. Decoded Trace Post-processing + The decoded trace is post-processed to reconstruct the execution flow of + the application. The execution flow contains the list of assembly + instructions (called instruction log hereafter). + + + + +============= +Dependencies +============= +The Tool has following hardware and software dependencies: + + - Hardware dependency: The Tool makes use of this hardware feature to capture + raw trace of an application from LLDB. This hardware feature may not be + present in all processors. The hardware feature is supported on Broadwell + and other succeeding CPUs such as Skylake etc. In order for Tool to provide + something meaningful, the target machine on which the application is running + should have this feature. + + - Software dependency: The Tool has an indirect dependency on the Operating + System level software support for Intel(R) Processor Trace on the target + machine where the application is running and being debugged by LLDB. This + support is required to enable raw trace generation on the target machine. + Currently, the Tool works for applications running on Linux OS as till now + the Operating System level support for the feature is present only in Linux + (more specifically starting from the 4.1 kernel). In Linux, this feature is + implemented in perf_events subsystem and is usable through perf_event_open + system call. In the User space level, the Tool has a direct dependency on + "libipt" to decode the captured raw trace. This library might be + pre-installed on host systems. If not then the library can be built from + its sources (available at): https://github.com/01org/processor-trace + + + + +============ +How to Build +============ +The Tool has a cmake based build and can be built by specifying some extra flags +while building LLDB with cmake. The following cmake flags need to be provided to +build the Tool: + + - LLDB_BUILD_INTEL_PT - The flag enables building of the Tool along with lldb. + This flag defaults to "OFF" meaning the Tool is excluded while building + lldb. Set it to "ON" in order to build the Tool. + + - LIBIPT_INCLUDE_PATH - The flag specifies the directory where the header + file of "libipt" resides. If the library is not pre-installed on the host + system and is built directly from "libipt" project sources then this file + may either come as a part of the sources itself or will be generated in + build folder while building library. + + - LIBIPT_LIBRARY_PATH - The flag points to the location of "libipt" shared + library. + +The Tool currently works successfully with following versions of this library: + - v1.4, v1.5, v1.6 + + + +============ +How to Use +============ +The Tool's API are exposed as a C++ object oriented interface (file PTDecoder.h) +in a shared library. The main class that implements the whole functionality is +PTDecoder. This class makes use of 2 other classes, one to return instruction +log (class PTInstruction) and the other to return trace specific information +(class PTInfo). The users can use these API to develop their own products. All +API are also available as python functions through a script bridging interface, +allowing them to be used directly from python either interactively or to build +python apps. + +Currently, cli wrapper has been developed on top of the Tool to use it through +LLDB's command line. Please refer to README_CLI.txt file for command line usage. + + +A brief introduction about the classes and their API are given below. + + class PTDecoder + =============== + This class makes use of Intel(R) Processor Trace hardware feature + (implemented 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 + + Corresponding API are explained below: + a) void StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API allows the user to start trace on a particular thread or on + the whole process with Intel(R) Processor Trace specific + configuration options. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[out] sberror : An error with the failure reason if API + fails. Else success. + + @param[in] sbtraceoptions : Contains thread id information and + configuration options: + For tracing a single thread, provide a valid thread id. If + sbprocess doesn't contain this thread id, error will be returned. + For tracing complete process, set to lldb::LLDB_INVALID_THREAD_ID + Configuration options comprises of: + - trace buffer size, meta data buffer size, TraceType and + - All other possible Intel(R) Processor Trace specific + configuration options (hereafter collectively referred as + CUSTOM_OPTIONS) + + Trace buffer, meant to store the trace data read from target + machine, inside LLDB is configured as a cyclic buffer. Hence, + depending upon the trace buffer size provided here, buffer + overwrites may happen while LLDB writes trace data into it. + CUSTOM_OPTIONS are formatted as json text i.e. {"Name":Value, + "Name":Value,...} inside sbtraceoptions, where "Value" should be + a 64-bit unsigned integer in hex format. For information + regarding what all configuration options are currently supported + by LLDB and detailed information about CUSTOM_OPTIONS usage, + please refer to SBProcess::StartTrace() API description. An + overview of some of the various CUSTOM_OPTIONS are briefly given + below. Please refer to "Intel(R) 64 and IA-32 Architectures + Software Developer's Manual" for more details about them. + - CYCEn Enable/Disable Cycle Count Packet (CYC) Packet + - OS Packet generation enabled/disabled if + Current Privilege Level (CPL)=0 + - User Packet generation enabled/disabled if CPL>0 + - CR3Filter Enable/Disable CR3 Filtering + - MTCEn Enable/disable MTC packets + - TSCEn Enable/disable TSC packets + - DisRETC Enable/disable RET Compression + - BranchEn Enable/disable COFI-based packets + - MTCFreq Defines MTC Packet Frequency + - CycThresh CYC Packet threshold + - PSBFreq Frequency of PSB Packets + + TraceType should be set to + lldb::TraceType::eTraceTypeProcessorTrace, else error is + returned. To find out any other requirement to start tracing + successfully, refer to SBProcess::StartTrace() API description. + LLDB's current implementation of Intel(R) Processor Trace + feature may round off invalid values for configuration options. + Therefore, the configuration options with which the trace was + actually started, might be different to the ones with which + trace was asked to be started by user. The actual used + configuration options can be obtained from + GetProcessorTraceInfo() API. + + + + b) void StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID) + ------------------------------------------------------------------------ + This API allows the user to Stop trace on a particular thread or on + the whole process. + + @param[in] sbprocess : A valid process on which this operation will + be performed. An error is returned in case of an invalid process. + + @param[in] tid : To stop tracing a single thread, provide a + valid thread id. If sbprocess doesn't contain the thread tid, + error will be returned. To stop tracing complete process, use + lldb::LLDB_INVALID_THREAD_ID + + @param[out] sberror : An error with the failure reason if API fails. + Else success + + + + c) void GetInstructionLogAtOffset(lldb::SBProcess &sbprocess, lldb::tid_t tid, + uint32_t offset, uint32_t count, + std::vector &result_list, + lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides instruction log that contains the execution flow + for a thread of a process in terms of assembly instruction executed. + The API works on only 1 thread at a time. To gather this information + for whole process, this API needs to be called for each thread. + + @param[in] sbprocess : A valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which + instruction log is desired. If sbprocess doesn't contain the + thread tid, error will be returned. + + @param[in] count : Number of instructions requested by the + user to be returned from the complete instruction log. Complete + instruction log refers to all the assembly instructions obtained + after decoding the complete raw trace data obtained from LLDB. + The length of the complete instruction log is dependent on the + trace buffer size with which processor tracing was started for + this thread. + The number of instructions actually returned are dependent on + 'count' and 'offset' parameters of this API. + + @param[in] offset : The offset in the complete instruction log + from where 'count' number of instructions are requested by the + user. offset is counted from the end of of this complete + instruction log (which means the last executed instruction + is at offset 0 (zero)). + + @param[out] result_list : Depending upon 'count' and 'offset' values, + instructions will be appended to the end of this list. + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + + d) void GetProcessorTraceInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + PTInfo &pt_info, lldb::SBError &sberror) + ------------------------------------------------------------------------ + This API provides Intel(R) Processor Trace specific information for + a thread of a process. The API works on only 1 thread at a time. To + gather this information for whole process, this API needs to be + called for each thread. The information contains the actual + configuration options with which the trace was started for this + thread. + + @param[in] sbprocess : The valid process on which this operation + will be performed. An error is returned in case of an invalid + process. + + @param[in] tid : A valid thread id of the thread for which the + trace specific information is required. If sbprocess doesn't + contain the thread tid, an error will be returned. + + @param[out] pt_info : Contains actual configuration options (they + may be different to the ones with which tracing was asked to be + started for this thread during StartProcessorTrace() API call). + + @param[out] sberror : An error with the failure reason if API + fails. Else success + + + class PTInstruction + ================= + This class represents an assembly instruction containing raw instruction + bytes, instruction address along with execution flow context and + Intel(R) Processor Trace context. For more details, please refer to + PTDecoder.h file. + + + class PTInfo + ============ + This class provides Intel(R) Processor Trace specific configuration + options like trace type, trace buffer size, meta data buffer size along + with other trace specific options. For more details, please refer to + PTDecoder.h file. Index: tools/intel-pt/cli-wrapper.cpp =================================================================== --- /dev/null +++ tools/intel-pt/cli-wrapper.cpp @@ -0,0 +1,586 @@ +//===-- cli-wrapper.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// CLI Wrapper of PTDecoder Tool to enable it to be used through LLDB's CLI. The +// wrapper provides a new command called processor-trace with 4 child +// subcommands as follows: +// processor-trace start +// processor-trace stop +// processor-trace show-info +// processor-trace show-instr-log +// +// Compile this into a shared lib and load by placing at appropriate locations +// on disk or by using "plugin load" command at the LLDB command line. +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include + +#include "PTDecoder.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStructuredData.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" + +namespace lldb { +bool PluginInitialize(lldb::SBDebugger debugger); +} + +static bool GetProcess(lldb::SBDebugger &debugger, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process) { + if (!debugger.IsValid()) { + result.Printf("error: invalid debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + lldb::SBTarget target = debugger.GetSelectedTarget(); + if (!target.IsValid()) { + result.Printf("error: invalid target inside debugger\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + process = target.GetProcess(); + if (!process.IsValid() || + (process.GetState() == lldb::StateType::eStateDetached) || + (process.GetState() == lldb::StateType::eStateExited) || + (process.GetState() == lldb::StateType::eStateInvalid)) { + result.Printf("error: invalid process inside debugger's target\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + return true; +} + +static bool ParseCommandOption(char **command, + lldb::SBCommandReturnObject &result, + uint32_t &index, const std::string &arg, + uint32_t &parsed_result) { + char *endptr; + if (!command[++index]) { + result.Printf("error: option \"%s\" requires an argument\n", arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0')) { + result.Printf("error: invalid value \"%s\" provided for option \"%s\"\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + if (output > UINT32_MAX) { + result.Printf("error: value \"%s\" for option \"%s\" exceeds UINT32_MAX\n", + command[index], arg.c_str()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + parsed_result = (uint32_t)output; + return true; +} + +static bool ParseCommandArgThread(char **command, + lldb::SBCommandReturnObject &result, + lldb::SBProcess &process, uint32_t &index, + lldb::tid_t &thread_id) { + char *endptr; + if (!strcmp(command[index], "all")) + thread_id = LLDB_INVALID_THREAD_ID; + else { + uint32_t thread_index_id; + errno = 0; + unsigned long output = strtoul(command[index], &endptr, 0); + if ((errno != 0) || (*endptr != '\0') || (output > UINT32_MAX)) { + result.Printf("error: invalid thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_index_id = (uint32_t)output; + + lldb::SBThread thread = process.GetThreadByIndexID(thread_index_id); + if (!thread.IsValid()) { + result.Printf( + "error: process has no thread with thread specification: \"%s\"\n", + command[index]); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + return true; +} + +class ProcessorTraceStart : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStart(std::shared_ptr &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStart() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + lldb::SBTraceOptions lldb_SBTraceOptions; + uint32_t trace_buffer_size = m_default_trace_buff_size; + lldb::tid_t thread_id; + + // Parse Command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-b")) { + if (!ParseCommandOption(command, result, i, "-b", trace_buffer_size)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + if (trace_buffer_size > m_max_trace_buff_size) + trace_buffer_size = m_max_trace_buff_size; + + // Set API's arguments with parsed values + lldb_SBTraceOptions.setType(lldb::TraceType::eTraceTypeProcessorTrace); + lldb_SBTraceOptions.setTraceBufferSize(trace_buffer_size); + lldb_SBTraceOptions.setMetaDataBufferSize(0); + lldb_SBTraceOptions.setThreadID(thread_id); + lldb::SBStream sb_stream; + sb_stream.Printf("{\"trace-tech\":\"intel-pt\"}"); + lldb::SBStructuredData custom_params; + lldb::SBError error = custom_params.SetFromJSON(sb_stream); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + lldb_SBTraceOptions.setTraceParams(custom_params); + + // Start trace + pt_decoder_sp->StartProcessorTrace(process, lldb_SBTraceOptions, error); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr pt_decoder_sp; + const uint32_t m_max_trace_buff_size = 0x3fff; + const uint32_t m_default_trace_buff_size = 4096; +}; + +class ProcessorTraceInfo : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceInfo(std::shared_ptr &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceInfo() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get trace information + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + ptdecoder::PTInfo pt_info; + pt_decoder_sp->GetProcessorTraceInfo(process, thread_id, pt_info, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStructuredData data = pt_info.GetTraceParams(error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + lldb::SBStream s; + error = data.GetAsJSON(s); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 + ", trace buffer size=%" PRIu64 ", meta buffer size=%" PRIu64 + ", trace type=%" PRIu32 ", custom trace params=%s", + thread.GetIndexID(), thread_id, pt_info.GetTraceBufferSize(), + pt_info.GetMetaDataBufferSize(), pt_info.GetType(), + s.GetData()); + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr pt_decoder_sp; +}; + +class ProcessorTraceShowInstrLog : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceShowInstrLog(std::shared_ptr &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceShowInstrLog() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + // Default initialize API's arguments + uint32_t offset; + bool offset_provided = false; + uint32_t count = m_default_count; + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + if (!strcmp(command[i], "-o")) { + if (!ParseCommandOption(command, result, i, "-o", offset)) + return false; + offset_provided = true; + } else if (!strcmp(command[i], "-c")) { + if (!ParseCommandOption(command, result, i, "-c", count)) + return false; + } else { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + size_t loop_count = 1; + bool entire_process_tracing = false; + if (thread_id == LLDB_INVALID_THREAD_ID) { + entire_process_tracing = true; + loop_count = process.GetNumThreads(); + } + + // Get instruction log and disassemble it + lldb::SBError error; + lldb::SBCommandReturnObject res; + for (size_t i = 0; i < loop_count; i++) { + error.Clear(); + res.Clear(); + + if (entire_process_tracing) + thread = process.GetThreadAtIndex(i); + else + thread = process.GetThreadByID(thread_id); + thread_id = thread.GetThreadID(); + + // If offset is not provided then calculate a default offset (to display + // last 'count' number of instructions) + if (!offset_provided) + offset = count - 1; + + // Get the instruction log + std::vector insn_list; + pt_decoder_sp->GetInstructionLogAtOffset(process, thread_id, offset, + count, insn_list, error); + if (!error.Success()) { + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 ", error: %s", + thread.GetIndexID(), thread_id, error.GetCString()); + result.AppendMessage(res.GetOutput()); + continue; + } + + // Disassemble the instruction log + std::string disassembler_command("dis -c 1 -s "); + res.Printf("thread #%" PRIu32 ": tid=%" PRIu64 "\n", thread.GetIndexID(), + thread_id); + lldb::SBCommandInterpreter sb_cmnd_interpreter( + debugger.GetCommandInterpreter()); + lldb::SBCommandReturnObject result_obj; + for (size_t i = 0; i < insn_list.size(); i++) { + uint64_t addr = insn_list[i].GetInsnAddress(); + std::string error = insn_list[i].GetError(); + if (!error.empty()) { + res.AppendMessage(error.c_str()); + continue; + } + + result_obj.Clear(); + std::string complete_disassembler_command = + disassembler_command + std::to_string(addr); + sb_cmnd_interpreter.HandleCommand(complete_disassembler_command.c_str(), + result_obj, false); + std::string result_str(result_obj.GetOutput()); + if (result_str.empty()) { + lldb::SBCommandReturnObject output; + output.Printf(" Disassembly not found for address: %" PRIu64, addr); + res.AppendMessage(output.GetOutput()); + continue; + } + + // LLDB's disassemble command displays assembly instructions along with + // the names of the functions they belong to. Parse this result to + // display only the assembly instructions and not the function names + // in an instruction log + std::size_t first_new_line_index = result_str.find_first_of('\n'); + std::size_t last_new_line_index = result_str.find_last_of('\n'); + if (first_new_line_index != last_new_line_index) + res.AppendMessage((result_str.substr(first_new_line_index + 1, + last_new_line_index - + first_new_line_index - 1)) + .c_str()); + else + res.AppendMessage( + (result_str.substr(0, result_str.length() - 1)).c_str()); + } + result.AppendMessage(res.GetOutput()); + } + return true; + } + +private: + std::shared_ptr pt_decoder_sp; + const uint32_t m_default_count = 10; +}; + +class ProcessorTraceStop : public lldb::SBCommandPluginInterface { +public: + ProcessorTraceStop(std::shared_ptr &pt_decoder) + : SBCommandPluginInterface(), pt_decoder_sp(pt_decoder) {} + + ~ProcessorTraceStop() {} + + virtual bool DoExecute(lldb::SBDebugger debugger, char **command, + lldb::SBCommandReturnObject &result) { + lldb::SBProcess process; + lldb::SBThread thread; + if (!GetProcess(debugger, result, process)) + return false; + + lldb::tid_t thread_id; + + // Parse command line options + bool thread_argument_provided = false; + if (command) { + for (uint32_t i = 0; command[i]; i++) { + thread_argument_provided = true; + if (!ParseCommandArgThread(command, result, process, i, thread_id)) + return false; + } + } + + if (!thread_argument_provided) { + thread = process.GetSelectedThread(); + if (!thread.IsValid()) { + result.Printf("error: invalid current selected thread\n"); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + thread_id = thread.GetThreadID(); + } + + // Stop trace + lldb::SBError error; + pt_decoder_sp->StopProcessorTrace(process, error, thread_id); + if (!error.Success()) { + result.Printf("error: %s\n", error.GetCString()); + result.SetStatus(lldb::eReturnStatusFailed); + return false; + } + return true; + } + +private: + std::shared_ptr pt_decoder_sp; +}; + +bool lldb::PluginInitialize(lldb::SBDebugger debugger) { + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommand proc_trace = interpreter.AddMultiwordCommand( + "processor-trace", "Intel(R) Processor Trace for thread/process"); + + std::shared_ptr PTDecoderSP( + new ptdecoder::PTDecoder(debugger)); + + lldb::SBCommandPluginInterface *proc_trace_start = + new ProcessorTraceStart(PTDecoderSP); + const char *help_proc_trace_start = "start Intel(R) Processor Trace on a " + "specific thread or on the whole process"; + const char *syntax_proc_trace_start = + "processor-trace start \n\n" + "\rcmd-options Usage:\n" + "\r processor-trace start [-b ] []\n\n" + "\t\b-b \n" + "\t size of the trace buffer to store the trace data. If not " + "specified then a default value will be taken\n\n" + "\t\b\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to start tracing the whole process\n"; + proc_trace.AddCommand("start", proc_trace_start, help_proc_trace_start, + syntax_proc_trace_start); + + lldb::SBCommandPluginInterface *proc_trace_stop = + new ProcessorTraceStop(PTDecoderSP); + const char *help_proc_trace_stop = + "stop Intel(R) Processor Trace on a specific thread or on whole process"; + const char *syntax_proc_trace_stop = + "processor-trace stop \n\n" + "\rcmd-options Usage:\n" + "\r processor-trace stop []\n\n" + "\t\b\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to stop tracing the whole process\n"; + proc_trace.AddCommand("stop", proc_trace_stop, help_proc_trace_stop, + syntax_proc_trace_stop); + + lldb::SBCommandPluginInterface *proc_trace_show_instr_log = + new ProcessorTraceShowInstrLog(PTDecoderSP); + const char *help_proc_trace_show_instr_log = + "display a log of assembly instructions executed for a specific thread " + "or for the whole process.\n" + "The length of the log to be displayed and the offset in the whole " + "instruction log from where the log needs to be displayed can also be " + "provided. The offset is counted from the end of this whole " + "instruction log which means the last executed instruction is at offset " + "0 (zero)"; + const char *syntax_proc_trace_show_instr_log = + "processor-trace show-instr-log \n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-instr-log [-o ] [-c ] " + "[]\n\n" + "\t\b-o \n" + "\t offset in the whole instruction log from where the log will be " + "displayed. If not specified then a default value will be taken\n\n" + "\t\b-c \n" + "\t number of instructions to be displayed. If not specified then a " + "default value will be taken\n\n" + "\t\b\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to show instruction log for all the " + "threads of the process\n"; + proc_trace.AddCommand("show-instr-log", proc_trace_show_instr_log, + help_proc_trace_show_instr_log, + syntax_proc_trace_show_instr_log); + + lldb::SBCommandPluginInterface *proc_trace_info = + new ProcessorTraceInfo(PTDecoderSP); + const char *help_proc_trace_show_info = + "display all the information regarding Intel(R) Processor Trace for a " + "specific thread or for the whole process.\n" + "The information contains trace buffer size and configuration options" + " of Intel(R) Processor Trace."; + const char *syntax_proc_trace_show_info = + "processor-trace show-info \n\n" + "\rcmd-options Usage:\n" + "\r processor-trace show-info []\n\n" + "\t\b\n" + "\t thread index of the thread. If no threads are specified, " + "currently selected thread is taken.\n" + "\t Use the thread-index 'all' to display information for all threads " + "of the process\n"; + proc_trace.AddCommand("show-info", proc_trace_info, help_proc_trace_show_info, + syntax_proc_trace_show_info); + + return true; +} Index: tools/intel-pt/scripts/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-pt/scripts/CMakeLists.txt @@ -0,0 +1,37 @@ +file(GLOB_RECURSE SWIG_SOURCES *.swig) + +set(FLAGS + -c++ + -shadow + -python + -D__STDC_LIMIT_MACROS + -D__STDC_CONSTANT_MACROS + ) + +set(INCLUDES + -I${LLDB_SOURCE_DIR}/include + -I${LLDB_SOURCE_DIR}/tools/intel-pt + ) + +set(OUTPUT_PYTHON_WRAPPER + ${CMAKE_CURRENT_BINARY_DIR}/IntelPTWrapPython.cpp + ) + +set(OUTPUT_PYTHON_SCRIPT_DIR + ${CMAKE_CURRENT_BINARY_DIR} + ) + +find_package(SWIG REQUIRED) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/IntelPTWrapPython.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelPT.py + DEPENDS ${SWIG_SOURCES} + COMMAND ${SWIG_EXECUTABLE} ${FLAGS} ${INCLUDES} -o ${OUTPUT_PYTHON_WRAPPER} -outdir ${OUTPUT_PYTHON_SCRIPT_DIR} ${SWIG_SOURCES} + COMMENT "Generating python wrapper for Intel(R) Processor Trace Tool") + +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/IntelPTWrapPython.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldbIntelPT.py PROPERTIES GENERATED 1) + +add_custom_target(intel-pt-swig_wrapper ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelPTWrapPython.cpp + ) Index: tools/intel-pt/scripts/lldb-intel-pt.swig =================================================================== --- /dev/null +++ tools/intel-pt/scripts/lldb-intel-pt.swig @@ -0,0 +1,33 @@ +%module lldbIntelPT + +%{ +#include "lldb/lldb-public.h" +#include "PTDecoder.h" +#include "../source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h" +using namespace ptdecoder; +%} + + +/* Various liblldb typedefs that SWIG needs to know about.*/ +#define __extension__ /* Undefine GCC keyword to make Swig happy when + processing glibc's stdint.h. */ +/* The ISO C99 standard specifies that in C++ implementations limit macros such + as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */ +#define __STDC_LIMIT_MACROS +%include "stdint.i" +%include "std_string.i" + + +%include "../../../scripts/Python/python-typemaps.swig" + +%include "lldb/lldb-defines.h" +%include "lldb/lldb-enumerations.h" +%include "lldb/lldb-forward.h" +%include "lldb/lldb-types.h" + +%include "lldb/API/SBDefines.h" + +%include "../PTDecoder.h" + +%include "std_vector.i" +%template(PTInstructions) std::vector;