Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -8,4 +8,4 @@ if (LLDB_CAN_USE_LLDB_SERVER) add_subdirectory(lldb-server) endif() -add_subdirectory(intel-mpx) +add_subdirectory(intel-features) Index: tools/intel-features/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-features/CMakeLists.txt @@ -0,0 +1,55 @@ +include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) + +LIST(APPEND COMMON_LINK_LIBS liblldb) +LIST(APPEND PT_DEPS "") +LIST(APPEND MPX_DEPS "") + +option(LLDB_BUILD_INTEL_MPX "Enable Building of Intel(R) Memory Protection Extensions" ON) +if (LLDB_BUILD_INTEL_MPX) + add_subdirectory(intel-mpx) +endif() + +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 (NOT LLDB_DISABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + set(LLDB_INTEL_FEATURES_PYTHON_WRAP ${LLDB_BINARY_DIR}/tools/intel-features/scripts/IntelFeaturesPythonWrap.cpp) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} PROPERTIES GENERATED 1) + + if (CLANG_CL) + set_source_files_properties(${LLDB_INTEL_FEATURES_PYTHON_WRAP} + 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_FEATURES_PYTHON_WRAP} + APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-sequence-point -Wno-cast-qual") + endif () + add_subdirectory(scripts) +endif() + +add_library(lldbIntelFeatures SHARED + cli-wrapper.cpp + ${LLDB_INTEL_FEATURES_PYTHON_WRAP} + ) + +if (LLDB_BUILD_INTEL_PT) + target_link_libraries(lldbIntelFeatures PUBLIC lldbIntelPT ${PT_DEPS}) +endif() + +if (LLDB_BUILD_INTEL_MPX) + target_link_libraries(lldbIntelFeatures PUBLIC lldbIntelMPX ${MPX_DEPS}) +endif() + +target_link_libraries(lldbIntelFeatures PUBLIC ${COMMON_LINK_LIBS}) + +if (NOT LLDB_DISABLE_PYTHON AND LLDB_BUILD_INTEL_PT) + add_dependencies(lldbIntelFeatures intel-features-swig_wrapper) + target_link_libraries(lldbIntelFeatures PRIVATE ${LLDB_SYSTEM_LIBS}) +endif() + +install(TARGETS lldbIntelFeatures + LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}) Index: tools/intel-features/README.txt =================================================================== --- /dev/null +++ tools/intel-features/README.txt @@ -0,0 +1,73 @@ +**************************************************************************** +* README * +* * +* This file provides all the information regarding new CLI commands that * +* enable using various hardware features of Intel(R) architecture based * +* processors from LLDB's CLI. * +**************************************************************************** + + +============ +Introduction +============ +A shared library has been developed to use various hardware features of +Intel(R) architecture based processors through LLDB's command line. The library +currently comprises of hardware features namely Intel(R) Processor Trace and +Intel(R) Memory Protection Extensions. + + +============ +Details +============ +A C++ based cli wrapper (cli-wrapper.cpp) has been developed here that +agglomerates all cli commands for various hardware features. This wrapper is +build to generate a shared library (lldbIntelFeatures) to provide all these +commands. + +For each hardware feature, separate cli commands have been developed that are +provided by wrappers (cli-wrapper-pt.cpp and cli-wrapper-mpxtable.cpp) residing +in feature specific folders ("intel-pt" and "intel-mpx" respectively). + +For details regarding cli commands of each feature, please refer to these +feature specific wrappers. + + + +============ +How to Build +============ +The shared library (lldbIntelFeatures) has a cmake based build and can be built +while building LLDB with cmake. "cli-wrapper.cpp" file is compiled along with all +the feature specific source files (residing in feature specific folders). + +Furthermore, flexibility is provided to the user to include/exclude a particular +feature while building lldbIntelFeatures library. This is done by flags described +below: + + - LLDB_BUILD_INTEL_PT - The flag enables building of Intel(R) Processor Trace + feature (inside intel-pt folder). This flag defaults to "OFF" meaning the + feature is excluded while building lldbIntelFeatures library. Set it to "ON" + in order to include it. + + - LLDB_BUILD_INTEL_MPX - Enables building Intel(R) Memory Protection Extensions + feature (inside intel-mpx folder). This flag defaults to "ON" meaning + the feature is excluded while building lldbIntelFeatures library. + +Please refer to README files in feature specific folders to know about additional +flags that need to be set in order to build that feature successfully. + + +============ +How to Use +============ +All CLI commands provided by this shared library can be used through the LLDB's +CLI by executing "plugin load " on LLDB CLI. shared_lib_name here +is lldbIntelFeatures + + + +============ +Description +============ +Please refer to README_CLI file of each feature to know about details of CLI +commands. Index: tools/intel-features/cli-wrapper.cpp =================================================================== --- /dev/null +++ tools/intel-features/cli-wrapper.cpp @@ -0,0 +1,30 @@ +//===-- 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 for hardware features of Intel(R) architecture based processors +// to enable them to be used through LLDB's CLI. For details, please refer to +// cli wrappers of each individual feature, residing in their respective +// folders. +// +// 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 "intel-mpx/cli-wrapper-mpxtable.h" +#include "intel-pt/cli-wrapper-pt.h" +#include "lldb/API/SBDebugger.h" + +namespace lldb { +bool PluginInitialize(lldb::SBDebugger debugger); +} + +bool lldb::PluginInitialize(lldb::SBDebugger debugger) { + PTPluginInitialize(debugger); + MPXPluginInitialize(debugger); + return true; +} Index: tools/intel-features/intel-mpx/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-features/intel-mpx/CMakeLists.txt @@ -0,0 +1,9 @@ +if (NOT CMAKE_SYSTEM_NAME MATCHES "Linux") + return () +endif () + +add_library(lldbIntelMPX + cli-wrapper-mpxtable.cpp + ) + +set(MPX_DEPS ${MPX_DEPS} LLVMSupport PARENT_SCOPE) Index: tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h =================================================================== --- /dev/null +++ tools/intel-features/intel-mpx/cli-wrapper-mpxtable.h @@ -0,0 +1,12 @@ +//===-- cli-wrapper-mpxtable.h----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDebugger.h" + +bool MPXPluginInitialize(lldb::SBDebugger& debugger); Index: tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp =================================================================== --- tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp +++ tools/intel-features/intel-mpx/cli-wrapper-mpxtable.cpp @@ -1,4 +1,4 @@ -//===-- IntelMPXTablePlugin.cpp----------------------------------*- C++ -*-===// +//===-- cli-wrapper-mpxtable.cpp----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,6 +12,7 @@ #include // Project includes +#include "cli-wrapper-mpxtable.h" #include "lldb/API/SBCommandInterpreter.h" #include "lldb/API/SBCommandReturnObject.h" #include "lldb/API/SBMemoryRegionInfo.h" @@ -21,10 +22,6 @@ #include "llvm/ADT/Triple.h" -namespace lldb { -bool PluginInitialize(lldb::SBDebugger debugger); -} - static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame, lldb::SBCommandReturnObject &result) { if (!cptr) { @@ -286,7 +283,8 @@ lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu"); if (!bndcfgu_val.IsValid()) { result.SetError( - "Cannot access register BNDCFGU. Does the target support MPX?"); + "Cannot access register BNDCFGU. Does the target support " + "Intel(R) Memory Protection Extensions (Intel(R) MPX)?"); result.SetStatus(lldb::eReturnStatusFailed); return false; } @@ -409,17 +407,17 @@ } }; -bool lldb::PluginInitialize(lldb::SBDebugger debugger) { +bool MPXPluginInitialize(lldb::SBDebugger& debugger) { lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand( - "mpx-table", "A utility to access the MPX table entries."); + "mpx-table", "A utility to access the Intel(R) MPX table entries."); - const char *mpx_show_help = "Show the MPX table entry of a pointer.\n" - "mpx-table show "; + const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer." + "\nmpx-table show "; mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help); const char *mpx_set_help = - "Set the MPX table entry of a pointer.\n" + "Set the Intel(R) MPX table entry of a pointer.\n" "mpx-table set "; mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help); Index: tools/intel-features/intel-mpx/test/README.txt =================================================================== --- tools/intel-features/intel-mpx/test/README.txt +++ tools/intel-features/intel-mpx/test/README.txt @@ -2,5 +2,5 @@ packages/Python/lldbsuite/test/functionalities/plugins/commands/mpxtablecmd -and copy into it the contents of this direcotry. +and copy into it the contents of this directory. Index: tools/intel-features/intel-mpx/test/TestMPXTable.py =================================================================== --- tools/intel-features/intel-mpx/test/TestMPXTable.py +++ tools/intel-features/intel-mpx/test/TestMPXTable.py @@ -24,16 +24,17 @@ @skipIf(compiler="clang") @skipIf(oslist=no_match(['linux'])) @skipIf(archs=no_match(['i386', 'x86_64'])) - @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports Intel(R) MPX. + @skipIf(compiler="gcc", compiler_version=["<", "5"]) #GCC version >= 5 supports + #Intel(R) Memory Protection Extensions (Intel(R) MPX). def test_show_command(self): """Test 'mpx-table show' command""" self.build() lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") - plugin_file = os.path.join(lldb_lib_dir, "liblldb-intel-mpxtable.so") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") if not os.path.isfile(plugin_file): - self.skipTest("Intel(R) mpx-table plugin missing.") + self.skipTest("features plugin missing.") plugin_command = " " seq = ("plugin", "load", plugin_file) plugin_command = plugin_command.join(seq) @@ -123,9 +124,9 @@ lldb_exec_dir = os.environ["LLDB_IMPLIB_DIR"] lldb_lib_dir = os.path.join(lldb_exec_dir, os.pardir, "lib") - plugin_file = os.path.join(lldb_lib_dir, "liblldb-intel-mpxtable.so") + plugin_file = os.path.join(lldb_lib_dir, "liblldbIntelFeatures.so") if not os.path.isfile(plugin_file): - self.skipTest("Intel(R) mpx-table plugin missing.") + self.skipTest("features plugin missing.") plugin_command = " " seq = ("plugin", "load", plugin_file) plugin_command = plugin_command.join(seq) Index: tools/intel-features/intel-mpx/test/main.cpp =================================================================== --- tools/intel-features/intel-mpx/test/main.cpp +++ tools/intel-features/intel-mpx/test/main.cpp @@ -32,7 +32,8 @@ int main(int argc, char const *argv[]) { - // This call returns 0 only if the CPU and the kernel support Intel(R) MPX. + // This call returns 0 only if the CPU and the kernel support + // Intel(R) Memory Protection Extensions (Intel(R) MPX). if (prctl(PR_MPX_ENABLE_MANAGEMENT, 0, 0, 0, 0) != 0) return -1; Index: tools/intel-features/intel-pt/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/CMakeLists.txt @@ -0,0 +1,29 @@ +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 + PTDecoder.cpp + Decoder.cpp + cli-wrapper-pt.cpp + ) + +set(PT_DEPS ${PT_DEPS} ${LIBIPT_LIBRARY} PARENT_SCOPE) Index: tools/intel-features/intel-pt/Decoder.h =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/Decoder.h @@ -0,0 +1,292 @@ +//===-- 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 TraceOptions +/// @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 TraceOptions : public lldb::SBTraceOptions { +public: + TraceOptions() : lldb::SBTraceOptions(), m_insn_log_size(0) {} + + ~TraceOptions() {} + + //------------------------------------------------------------------ + /// Get total number of assembly instructions obtained after decoding the + /// complete Intel(R) Processor Trace data obtained from LLDB. + /// + /// @return + /// Total number of instructions. + //------------------------------------------------------------------ + uint32_t getInstructionLogSize() const { return m_insn_log_size; } + + //------------------------------------------------------------------ + /// Set total number of assembly instructions. + /// + /// @param[in] size + /// Value to be set. + //------------------------------------------------------------------ + void setInstructionLogSize(uint32_t size) { m_insn_log_size = size; } + +private: + uint32_t m_insn_log_size; +}; + +//---------------------------------------------------------------------- +/// @class Decoder +/// @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, + TraceOptions &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); + + // Function to remove entries of finished processes/threads in the class + void RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess); + + // Parse cpu information from trace configuration received from LLDB + void ParseCPUInfo(CPUInfo &pt_cpu, lldb::SBStructuredData &s, + lldb::SBError &sberror); + + ///------------------------------------------------------------------------ + /// Function performs following tasks for a given process and thread: + /// - Checks if the given thread is registered in the class or not. If not + /// then tries to register it if trace was ever started on the entire + /// process. Else returns error. + /// - fetches trace and other necessary information from LLDB (using + /// ReadTraceDataAndImageInfo()) and decodes the trace (using + /// DecodeProcessorTrace()) + ///------------------------------------------------------------------------ + void FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo); + + // Helper function of FetchAndDecode() to get raw trace data and memory image + // info of inferior from LLDB + void ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of FetchAndDecode() to initialize raw trace decoder and + // start trace decoding + void DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo); + + // Helper function of ReadTraceDataAndImageInfo() function for gathering + // inferior's memory image info along with all dynamic libraries linked with + // it + void GetTargetModulesInfo(lldb::SBTarget &sbtarget, + ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror); + + ///------------------------------------------------------------------------ + /// Helper functions of DecodeProcessorTrace() function for: + /// - initializing raw trace decoder (provided by Intel(R) Processor Trace + /// Decoding library) + /// - start trace decoding + ///------------------------------------------------------------------------ + void InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const; + void DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, lldb::SBError &sberror); + + // 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-features/intel-pt/Decoder.cpp =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/Decoder.cpp @@ -0,0 +1,938 @@ +//===-- 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 +#include + +// Other libraries and framework includes +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBThread.h" + +using namespace ptdecoder_private; + +// This function removes entries of all the processes/threads which were once +// registered in the class but are not alive anymore because they died or +// finished executing. +void Decoder::RemoveDeadProcessesAndThreads(lldb::SBProcess &sbprocess) { + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + uint32_t num_targets = sbdebugger.GetNumTargets(); + + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.begin(); + while (itr_process != m_mapProcessUID_mapThreadID_TraceInfo.end()) { + bool process_found = false; + lldb::SBTarget target; + lldb::SBProcess process; + for (uint32_t i = 0; i < num_targets; i++) { + target = sbdebugger.GetTargetAtIndex(i); + process = target.GetProcess(); + if (process.GetUniqueID() == itr_process->first) { + process_found = true; + break; + } + } + + // Remove the process's entry if it was not found in SBDebugger + if (!process_found) { + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + continue; + } + + // If the state of the process is exited or detached then remove process's + // entry. If not then remove entry for all those registered threads of this + // process that are not alive anymore. + lldb::StateType state = process.GetState(); + if ((state == lldb::StateType::eStateDetached) || + (state == lldb::StateType::eStateExited)) + itr_process = m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + else { + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + if (itr_thread->first == LLDB_INVALID_THREAD_ID) { + ++itr_thread; + continue; + } + + lldb::SBThread thread = process.GetThreadByID(itr_thread->first); + if (!thread.IsValid()) + itr_thread = itr_process->second.erase(itr_thread); + else + ++itr_thread; + } + ++itr_process; + } + } +} + +void Decoder::StartProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBTraceOptions &sbtraceoptions, + lldb::SBError &sberror) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) + return; + + std::lock_guard guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + if (sbtraceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("SBTraceOptions::TraceType not set to " + "eTraceTypeProcessorTrace; ProcessID = " + "%" PRIu64, + sbprocess.GetProcessID()); + return; + } + lldb::SBStructuredData sbstructdata = sbtraceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + + const char *trace_tech_key = "trace-tech"; + std::string trace_tech_value("intel-pt"); + lldb::SBStructuredData value = sbstructdata.GetValueForKey(trace_tech_key); + if (!value.IsValid()) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + char string_value[9]; + size_t bytes_written = value.GetStringValue( + string_value, sizeof(string_value) / sizeof(*string_value)); + if (!bytes_written || + (bytes_written > (sizeof(string_value) / sizeof(*string_value)))) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set in custom trace parameters", trace_tech_key); + return; + } + + std::size_t pos = + trace_tech_value.find((const char *)string_value, 0, bytes_written); + if ((pos == std::string::npos)) { + sberror.SetErrorStringWithFormat( + "key \"%s\" not set to \"%s\" in custom trace parameters", + trace_tech_key, trace_tech_value.c_str()); + return; + } + + // Start Tracing + lldb::SBError error; + uint32_t unique_id = sbprocess.GetUniqueID(); + lldb::tid_t tid = sbtraceoptions.getThreadID(); + lldb::SBTrace trace = sbprocess.StartTrace(sbtraceoptions, error); + if (!error.Success()) { + if (tid == LLDB_INVALID_THREAD_ID) + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + else + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = + m_mapProcessUID_mapThreadID_TraceInfo[unique_id]; + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); +} + +void Decoder::StopProcessorTrace(lldb::SBProcess &sbprocess, + lldb::SBError &sberror, lldb::tid_t tid) { + sberror.Clear(); + CheckDebuggerID(sbprocess, sberror); + if (!sberror.Success()) { + return; + } + + std::lock_guard guard( + m_mapProcessUID_mapThreadID_TraceInfo_mutex); + RemoveDeadProcessesAndThreads(sbprocess); + + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBError error; + if (tid == LLDB_INVALID_THREAD_ID) { + // This implies to stop tracing on the whole process + lldb::user_id_t id_to_be_ignored = LLDB_INVALID_UID; + auto itr_thread = itr_process->second.begin(); + while (itr_thread != itr_process->second.end()) { + // In the case when user started trace on the entire process and then + // registered newly spawned threads of this process in the class later, + // these newly spawned threads will have same trace id. If we stopped + // trace on the entire process then tracing stops automatically for these + // newly spawned registered threads. Stopping trace on them again will + // return error and therefore we need to skip stopping trace on them + // again. + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + lldb::user_id_t lldb_pt_user_id = trace.GetTraceUID(); + if (lldb_pt_user_id != id_to_be_ignored) { + trace.StopTrace(error, itr_thread->first); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if ((error_string.find("tracing not active for this process") == + std::string::npos) && + (error_string.find("tracing not active for this thread") == + std::string::npos)) { + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), itr_thread->first, + sbprocess.GetProcessID()); + return; + } + } + + if (itr_thread->first == LLDB_INVALID_THREAD_ID) + id_to_be_ignored = lldb_pt_user_id; + } + itr_thread = itr_process->second.erase(itr_thread); + } + m_mapProcessUID_mapThreadID_TraceInfo.erase(itr_process); + } else { + // This implies to stop tracing on a single thread. + // if 'tid' is registered in the class then get the trace id and stop trace + // on it. If it is not then check if tracing was ever started on the entire + // process (because there is a possibility that trace is still running for + // 'tid' but it was not registered in the class because user had started + // trace on the whole process and 'tid' spawned later). In that case, get + // the trace id of the process trace instance and stop trace on this thread. + // If tracing was never started on the entire process then return error + // because there is no way tracing is active on 'tid'. + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + lldb::SBTrace trace; + auto itr = mapThreadID_TraceInfo.find(tid); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + auto itr = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr != mapThreadID_TraceInfo.end()) { + trace = itr->second.GetUniqueTraceInstance(); + } else { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + } + + // Stop Tracing + trace.StopTrace(error, tid); + if (!error.Success()) { + std::string error_string(error.GetCString()); + sberror.SetErrorStringWithFormat( + "%s; thread id=%" PRIu64 ", ProcessID = %" PRIu64, + error_string.c_str(), tid, sbprocess.GetProcessID()); + if (error_string.find("tracing not active") == std::string::npos) + return; + } + // Delete the entry of 'tid' from this class (if any) + mapThreadID_TraceInfo.erase(tid); + } +} + +void Decoder::ReadTraceDataAndImageInfo(lldb::SBProcess &sbprocess, + lldb::tid_t tid, lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Allocate trace data buffer and parse cpu info for 'tid' if it is registered + // for the first time in class + lldb::SBTrace &trace = threadTraceInfo.GetUniqueTraceInstance(); + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + lldb::SBError error; + if (pt_buffer.size() == 0) { + lldb::SBTraceOptions traceoptions; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error.GetCString(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + threadTraceInfo.AllocatePTBuffer(traceoptions.getTraceBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ParseCPUInfo(pt_cpu, sbstructdata, sberror); + if (!sberror.Success()) + return; + } + + // Call LLDB API to get raw trace data for this thread + size_t bytes_written = trace.GetTraceData(error, (void *)pt_buffer.data(), + pt_buffer.size(), 0, tid); + if (!error.Success()) { + sberror.SetErrorStringWithFormat( + "%s; thread_id = %" PRIu64 ", ProcessID = %" PRIu64, + error.GetCString(), tid, sbprocess.GetProcessID()); + return; + } + std::fill(pt_buffer.begin() + bytes_written, pt_buffer.end(), 0); + + // Get information of all the modules of the inferior + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + GetTargetModulesInfo(sbtarget, readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; +} + +void Decoder::DecodeProcessorTrace(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo &threadTraceInfo) { + // Initialize instruction decoder + struct pt_insn_decoder *decoder = nullptr; + struct pt_config config; + Buffer &pt_buffer = threadTraceInfo.GetPTBuffer(); + CPUInfo &pt_cpu = threadTraceInfo.GetCPUInfo(); + ReadExecuteSectionInfos &readExecuteSectionInfos = + threadTraceInfo.GetReadExecuteSectionInfos(); + + InitializePTInstDecoder(&decoder, &config, pt_cpu, pt_buffer, + readExecuteSectionInfos, sberror); + if (!sberror.Success()) + return; + + // Start raw trace decoding + Instructions &instruction_list = threadTraceInfo.GetInstructionLog(); + instruction_list.clear(); + DecodeTrace(decoder, instruction_list, sberror); +} + +// Raw trace decoding requires information of Read & Execute sections of each +// module of the inferior. This function updates internal state of the class to +// store this information. +void Decoder::GetTargetModulesInfo( + lldb::SBTarget &sbtarget, ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) { + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat("Can't get target's modules info from " + "LLDB; process has an invalid target"); + return; + } + + lldb::SBFileSpec target_file_spec = sbtarget.GetExecutable(); + if (!target_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat("Target has an invalid file spec"); + return; + } + + uint32_t num_modules = sbtarget.GetNumModules(); + readExecuteSectionInfos.clear(); + + // Store information of all RX sections of each module of inferior + for (uint32_t i = 0; i < num_modules; i++) { + lldb::SBModule module = sbtarget.GetModuleAtIndex(i); + if (!module.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid module", + i, target_file_spec.GetFilename()); + return; + } + + lldb::SBFileSpec module_file_spec = module.GetPlatformFileSpec(); + if (!module_file_spec.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 + " ] of target \"%s\" from LLDB, invalid file spec", + i, target_file_spec.GetFilename()); + return; + } + + const char *image(module_file_spec.GetFilename()); + lldb::SBError error; + char image_complete_path[1024]; + uint32_t path_length = module_file_spec.GetPath( + image_complete_path, sizeof(image_complete_path)); + size_t num_sections = module.GetNumSections(); + + // Store information of only RX sections + for (size_t idx = 0; idx < num_sections; idx++) { + lldb::SBSection section = module.GetSectionAtIndex(idx); + uint32_t section_permission = section.GetPermissions(); + if ((section_permission & lldb::Permissions::ePermissionsReadable) && + (section_permission & lldb::Permissions::ePermissionsExecutable)) { + lldb::SBData section_data = section.GetSectionData(); + if (!section_data.IsValid()) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, invalid " + "data in \"%s\" section", + i, image, target_file_spec.GetFilename(), section.GetName()); + return; + } + + // In case section has no data, skip it. + if (section_data.GetByteSize() == 0) + continue; + + if (!path_length) { + sberror.SetErrorStringWithFormat( + "Can't get module info [ %" PRIu32 " ] \"%s\" of target " + "\"%s\" from LLDB, module " + "has an invalid path length", + i, image, target_file_spec.GetFilename()); + return; + } + + std::string image_path(image_complete_path, path_length); + 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::SBStructuredData &s, + lldb::SBError &sberror) { + lldb::SBStructuredData custom_trace_params = s.GetValueForKey("intel-pt"); + if (!custom_trace_params.IsValid()) { + sberror.SetErrorStringWithFormat("lldb couldn't provide cpuinfo"); + return; + } + + uint64_t family = 0, model = 0, stepping = 0; + char vendor[32]; + const char *key_family = "cpu_family"; + const char *key_model = "cpu_model"; + const char *key_stepping = "cpu_stepping"; + const char *key_vendor = "cpu_vendor"; + + // parse family + lldb::SBStructuredData struct_family = + custom_trace_params.GetValueForKey(key_family); + if (!struct_family.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters", key_family); + return; + } + family = struct_family.GetIntegerValue(0x10000); + if (family > UINT16_MAX) { + sberror.SetErrorStringWithFormat( + "invalid CPU family value extracted from custom trace parameters"); + return; + } + pt_cpu.family = (uint16_t)family; + + // parse model + lldb::SBStructuredData struct_model = + custom_trace_params.GetValueForKey(key_model); + if (!struct_model.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16, + key_model, pt_cpu.family); + return; + } + model = struct_model.GetIntegerValue(0x100); + if (model > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU model value extracted from " + "custom trace parameters; family=%" PRIu16, + pt_cpu.family); + return; + } + pt_cpu.model = (uint8_t)model; + + // parse stepping + lldb::SBStructuredData struct_stepping = + custom_trace_params.GetValueForKey(key_stepping); + if (!struct_stepping.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8, + key_stepping, pt_cpu.family, pt_cpu.model); + return; + } + stepping = struct_stepping.GetIntegerValue(0x100); + if (stepping > UINT8_MAX) { + sberror.SetErrorStringWithFormat("invalid CPU stepping value extracted " + "from custom trace parameters; " + "family=%" PRIu16 ", model=%" PRIu8, + pt_cpu.family, pt_cpu.model); + return; + } + pt_cpu.stepping = (uint8_t)stepping; + + // parse vendor info + pt_cpu.vendor = pcv_unknown; + lldb::SBStructuredData struct_vendor = + custom_trace_params.GetValueForKey(key_vendor); + if (!struct_vendor.IsValid()) { + sberror.SetErrorStringWithFormat( + "%s info missing in custom trace parameters; family=%" PRIu16 + ", model=%" PRIu8 ", stepping=%" PRIu8, + key_vendor, pt_cpu.family, pt_cpu.model, pt_cpu.stepping); + return; + } + auto length = struct_vendor.GetStringValue(vendor, sizeof(vendor)); + if (length && strstr(vendor, "GenuineIntel")) + pt_cpu.vendor = pcv_intel; +} + +// Initialize trace decoder with pt_config structure and populate its image +// structure with inferior's memory image information. pt_config structure is +// initialized with trace buffer and cpu info of the inferior before storing it +// in trace decoder. +void Decoder::InitializePTInstDecoder( + struct pt_insn_decoder **decoder, struct pt_config *config, + const CPUInfo &pt_cpu, Buffer &pt_buffer, + const ReadExecuteSectionInfos &readExecuteSectionInfos, + lldb::SBError &sberror) const { + if (!decoder || !config) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + // Load cpu info of inferior's target in pt_config struct + pt_config_init(config); + config->cpu = pt_cpu; + int errcode = pt_cpu_errata(&(config->errata), &(config->cpu)); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_cpu_errata() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + return; + } + + // Load trace buffer's starting and end address in pt_config struct + config->begin = pt_buffer.data(); + config->end = pt_buffer.data() + pt_buffer.size(); + + // Fill trace decoder with pt_config struct + *decoder = pt_insn_alloc_decoder(config); + if (*decoder == nullptr) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_alloc_decoder() returned null " + "pointer"); + return; + } + + // Fill trace decoder's image with inferior's memory image information + struct pt_image *image = pt_insn_get_image(*decoder); + if (!image) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_insn_get_image() returned null " + "pointer"); + pt_insn_free_decoder(*decoder); + return; + } + + for (auto &itr : readExecuteSectionInfos) { + errcode = pt_image_add_file(image, itr.image_path.c_str(), itr.file_offset, + itr.size, nullptr, itr.load_address); + if (errcode < 0) { + sberror.SetErrorStringWithFormat("processor trace decoding library: " + "pt_image_add_file() failed with error: " + "\"%s\"", + pt_errstr(pt_errcode(errcode))); + pt_insn_free_decoder(*decoder); + return; + } + } +} + +// Start actual decoding of raw trace +void Decoder::DecodeTrace(struct pt_insn_decoder *decoder, + Instructions &instruction_list, + lldb::SBError &sberror) { + uint64_t decoder_offset = 0; + + while (1) { + struct pt_insn insn; + + // Try to sync the decoder. If it fails then get the decoder_offset and try + // to sync again. If the new_decoder_offset is same as decoder_offset then + // we will not succeed in syncing for any number of pt_insn_sync_forward() + // operations. Return in that case. Else keep resyncing until either end of + // trace stream is reached or pt_insn_sync_forward() passes. + int errcode = pt_insn_sync_forward(decoder); + if (errcode < 0) { + if (errcode == -pte_eos) + return; + + int errcode_off = pt_insn_get_offset(decoder, &decoder_offset); + if (errcode_off < 0) { + sberror.SetErrorStringWithFormat( + "processor trace decoding library: \"%s\"", + pt_errstr(pt_errcode(errcode))); + 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, + TraceOptions &options, + 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 'traceoptions' with it + lldb::SBTrace &trace = threadTraceInfo->GetUniqueTraceInstance(); + lldb::SBTraceOptions traceoptions; + lldb::SBError error; + traceoptions.setThreadID(tid); + trace.GetTraceConfig(traceoptions, error); + if (!error.Success()) { + std::string error_string(error.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) { + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) + return; + itr_process->second.erase(tid); + } + sberror.SetErrorStringWithFormat("%s; ProcessID = %" PRIu64, + error_string.c_str(), + sbprocess.GetProcessID()); + return; + } + if (traceoptions.getType() != lldb::TraceType::eTraceTypeProcessorTrace) { + sberror.SetErrorStringWithFormat("invalid TraceType received from LLDB " + "for this thread; thread id=%" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + options.setType(traceoptions.getType()); + options.setTraceBufferSize(traceoptions.getTraceBufferSize()); + options.setMetaDataBufferSize(traceoptions.getMetaDataBufferSize()); + lldb::SBStructuredData sbstructdata = traceoptions.getTraceParams(sberror); + if (!sberror.Success()) + return; + options.setTraceParams(sbstructdata); + options.setInstructionLogSize(threadTraceInfo->GetInstructionLog().size()); +} + +void Decoder::FetchAndDecode(lldb::SBProcess &sbprocess, lldb::tid_t tid, + lldb::SBError &sberror, + ThreadTraceInfo **threadTraceInfo) { + // Return with error if 'sbprocess' is not registered in the class + uint32_t unique_id = sbprocess.GetUniqueID(); + auto itr_process = m_mapProcessUID_mapThreadID_TraceInfo.find(unique_id); + if (itr_process == m_mapProcessUID_mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this process; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (tid == LLDB_INVALID_THREAD_ID) { + sberror.SetErrorStringWithFormat( + "invalid thread id provided; thread_id = %" PRIu64 + ", ProcessID = %" PRIu64, + tid, sbprocess.GetProcessID()); + return; + } + + // Check whether 'tid' thread is registered in the class. If it is then in + // case StopID didn't change then return without doing anything (no need to + // read and decode trace data then). Otherwise, save new StopID and proceed + // with reading and decoding trace. + if (threadTraceInfo == nullptr) { + sberror.SetErrorStringWithFormat("internal error"); + return; + } + + MapThreadID_TraceInfo &mapThreadID_TraceInfo = itr_process->second; + auto itr_thread = mapThreadID_TraceInfo.find(tid); + if (itr_thread != mapThreadID_TraceInfo.end()) { + if (itr_thread->second.GetStopID() == sbprocess.GetStopID()) { + *threadTraceInfo = &(itr_thread->second); + return; + } + itr_thread->second.SetStopID(sbprocess.GetStopID()); + } else { + // Implies 'tid' is not registered in the class. If tracing was never + // started on the entire process then return an error. Else try to register + // this thread and proceed with reading and decoding trace. + lldb::SBError error; + itr_thread = mapThreadID_TraceInfo.find(LLDB_INVALID_THREAD_ID); + if (itr_thread == mapThreadID_TraceInfo.end()) { + sberror.SetErrorStringWithFormat( + "tracing not active for this thread; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBTrace &trace = itr_thread->second.GetUniqueTraceInstance(); + ThreadTraceInfo &trace_info = mapThreadID_TraceInfo[tid]; + trace_info.SetUniqueTraceInstance(trace); + trace_info.SetStopID(sbprocess.GetStopID()); + itr_thread = mapThreadID_TraceInfo.find(tid); + } + + // Get raw trace data and inferior image from LLDB for the registered thread + ReadTraceDataAndImageInfo(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + std::string error_string(sberror.GetCString()); + if (error_string.find("tracing not active") != std::string::npos) + mapThreadID_TraceInfo.erase(itr_thread); + return; + } + // Decode raw trace data + DecodeProcessorTrace(sbprocess, tid, sberror, itr_thread->second); + if (!sberror.Success()) { + return; + } + *threadTraceInfo = &(itr_thread->second); +} + +// This function checks whether the provided SBProcess instance belongs to same +// SBDebugger with which this tool instance is associated. +void Decoder::CheckDebuggerID(lldb::SBProcess &sbprocess, + lldb::SBError &sberror) { + if (!sbprocess.IsValid()) { + sberror.SetErrorStringWithFormat("invalid process instance"); + return; + } + + lldb::SBTarget sbtarget = sbprocess.GetTarget(); + if (!sbtarget.IsValid()) { + sberror.SetErrorStringWithFormat( + "process contains an invalid target; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + lldb::SBDebugger sbdebugger = sbtarget.GetDebugger(); + if (!sbdebugger.IsValid()) { + sberror.SetErrorStringWithFormat("process's target contains an invalid " + "debugger instance; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } + + if (sbdebugger.GetID() != m_debugger_user_id) { + sberror.SetErrorStringWithFormat( + "process belongs to a different SBDebugger instance than the one for " + "which the tool is instantiated; ProcessID = %" PRIu64, + sbprocess.GetProcessID()); + return; + } +} Index: tools/intel-features/intel-pt/PTDecoder.h =================================================================== --- /dev/null +++ tools/intel-features/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 TraceOptions; +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 PTTraceOptions +/// @brief Provides configuration options like trace type, trace buffer size, +/// meta data buffer size along with other Intel(R) Processor Trace +/// specific options. +//---------------------------------------------------------------------- +class PTTraceOptions { +public: + PTTraceOptions(); + + PTTraceOptions(const PTTraceOptions &options); + + ~PTTraceOptions(); + + 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] options + /// 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, + PTTraceOptions &options, lldb::SBError &sberror); + +private: + std::shared_ptr m_opaque_sp; +}; + +} // namespace ptdecoder +#endif // PTDecoder_h_ Index: tools/intel-features/intel-pt/PTDecoder.cpp =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/PTDecoder.cpp @@ -0,0 +1,154 @@ +//===-- 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); +} + +// PTTraceOptions class member functions definitions +PTTraceOptions::PTTraceOptions() : m_opaque_sp() {} + +PTTraceOptions::PTTraceOptions(const PTTraceOptions &options) + : m_opaque_sp(options.m_opaque_sp) {} + +PTTraceOptions::~PTTraceOptions() {} + +lldb::TraceType PTTraceOptions::GetType() const { + return (m_opaque_sp ? m_opaque_sp->getType() + : lldb::TraceType::eTraceTypeNone); +} + +uint64_t PTTraceOptions::GetTraceBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getTraceBufferSize() : 0); +} + +uint64_t PTTraceOptions::GetMetaDataBufferSize() const { + return (m_opaque_sp ? m_opaque_sp->getMetaDataBufferSize() : 0); +} + +lldb::SBStructuredData PTTraceOptions::GetTraceParams(lldb::SBError &error) { + if (!m_opaque_sp) + error.SetErrorString("null pointer"); + return (m_opaque_sp ? m_opaque_sp->getTraceParams(error) + : lldb::SBStructuredData()); +} + +void PTTraceOptions::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, PTTraceOptions &options, + lldb::SBError &sberror) { + if (m_opaque_sp == nullptr) { + sberror.SetErrorStringWithFormat("invalid PTDecoder instance"); + return; + } + + std::shared_ptr trace_options_ptr( + new TraceOptions()); + m_opaque_sp->GetProcessorTraceInfo(sbprocess, tid, *trace_options_ptr, + sberror); + if (!sberror.Success()) + return; + + options.SetSP(trace_options_ptr); +} Index: tools/intel-features/intel-pt/README_CLI.txt =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/README_CLI.txt @@ -0,0 +1,123 @@ +**************************************************************************** +* 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-pt.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. The procedure to build shared library of the Intel(R) Processor Trace Tool +is given in README_TOOL.txt file. + + + +============ +How to Use +============ +All these commands are available via shared library (lldbIntelFeatures) +obtained after building intel-features folder from top. Please refer to +cli-wrapper.cpp and README files of "intel-features" folder for this purpose. + + + +============ +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-trace-options [] + + 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-trace-options + + cmd-options Usage: + processor-trace show-trace-options [] + + + 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-features/intel-pt/README_TOOL.txt =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/README_TOOL.txt @@ -0,0 +1,305 @@ +******************************************************************************* +* 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: + + - 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 PTTraceOptions). 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, + PTTraceOptions &options, 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] options : 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 PTTraceOptions + ============ + 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-features/intel-pt/interface/PTDecoder.i =================================================================== --- /dev/null +++ tools/intel-features/intel-pt/interface/PTDecoder.i @@ -0,0 +1,13 @@ +%include "stdint.i" +%include "std_string.i" +%include "std_vector.i" + +%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" +%template(PTInstructions) std::vector; Index: tools/intel-features/scripts/CMakeLists.txt =================================================================== --- /dev/null +++ tools/intel-features/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-features/intel-pt + ) + +set(OUTPUT_PYTHON_WRAPPER + ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) + +set(OUTPUT_PYTHON_SCRIPT_DIR + ${CMAKE_CURRENT_BINARY_DIR} + ) + +find_package(SWIG REQUIRED) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.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 features library") + +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp PROPERTIES GENERATED 1) +set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py PROPERTIES GENERATED 1) + +add_custom_target(intel-features-swig_wrapper ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp + ) Index: tools/intel-features/scripts/lldb-intel-features.swig =================================================================== --- /dev/null +++ tools/intel-features/scripts/lldb-intel-features.swig @@ -0,0 +1,18 @@ +%module lldbIntelFeatures + +%{ +#include "lldb/lldb-public.h" +#include "intel-pt/PTDecoder.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 "python-typemaps.txt" + +/* Feature specific python interface files*/ +%include "../intel-pt/interface/PTDecoder.i" Index: tools/intel-features/scripts/python-typemaps.txt =================================================================== --- /dev/null +++ tools/intel-features/scripts/python-typemaps.txt @@ -0,0 +1,15 @@ +/* Typemap definitions to allow SWIG to properly handle some data types */ + +// typemap for a char buffer +%typemap(in) (char *dst, size_t dst_len) { + if (!PyInt_Check($input)) { + PyErr_SetString(PyExc_ValueError, "Expecting an integer"); + return NULL; + } + $2 = PyInt_AsLong($input); + if ($2 <= 0) { + PyErr_SetString(PyExc_ValueError, "Positive integer expected"); + return NULL; + } + $1 = (char *) malloc($2); +} Index: tools/intel-mpx/CMakeLists.txt =================================================================== --- tools/intel-mpx/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -if (NOT CMAKE_SYSTEM_NAME MATCHES "Linux") - return () -endif () - -include(${LLDB_PROJECT_ROOT}/cmake/LLDBDependencies.cmake) - -add_library(lldb-intel-mpxtable SHARED - IntelMPXTablePlugin.cpp - ) - -target_link_libraries(lldb-intel-mpxtable - PUBLIC liblldb LLVMSupport) - -install(TARGETS lldb-intel-mpxtable - LIBRARY DESTINATION bin) Index: tools/intel-mpx/test/Makefile =================================================================== --- /dev/null +++ tools/intel-mpx/test/Makefile @@ -1,7 +0,0 @@ -LEVEL = ../../../../make - -CXX_SOURCES := main.cpp - -CFLAGS_EXTRAS += -mmpx -fcheck-pointer-bounds -lmpxwrappers -lmpx -fuse-ld=bfd - -include $(LEVEL)/Makefile.rules