diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -63,8 +63,10 @@ static llvm::sys::DynamicLibrary LoadPlugin(const lldb::DebuggerSP &debugger_sp, const FileSpec &spec, Status &error) { + std::string error_message; llvm::sys::DynamicLibrary dynlib = - llvm::sys::DynamicLibrary::getPermanentLibrary(spec.GetPath().c_str()); + llvm::sys::DynamicLibrary::getPermanentLibrary(spec.GetPath().c_str(), + &error_message); if (dynlib.isValid()) { typedef bool (*LLDBCommandPluginInit)(lldb::SBDebugger & debugger); @@ -89,7 +91,9 @@ } } else { if (FileSystem::Instance().Exists(spec)) - error.SetErrorString("this file does not represent a loadable dylib"); + error.SetErrorStringWithFormat( + "this file does not represent a loadable dylib. %s", + error_message.c_str()); else error.SetErrorString("no such file"); } diff --git a/lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py b/lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py --- a/lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py +++ b/lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py @@ -3,6 +3,7 @@ import os import lldb import time +import lldbIntelFeatures from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -24,6 +25,29 @@ plugin_path = os.path.join(os.environ["LLDB_IMPLIB_DIR"], "liblldbIntelFeatures.so") self.runCmd("plugin load " + plugin_path) + def waitUntilNotNone(self, callback): + obj = None + for _ in range(20): + obj = callback() + if obj: + break + time.sleep(0.5) + self.assertIsNotNone(obj) + return obj + + def waitUntilTrue(self, callback): + res = False + for _ in range(20): + res = callback() + if res: + break + time.sleep(0.5) + self.assertTrue(res) + + def find_start_address_of_function(self, target, function_name): + return target.FindFunctions(function_name)[0].GetSymbol() \ + .GetStartAddress().GetLoadAddress(target) + @skipIf(oslist=no_match(['linux'])) @skipIf(archs=no_match(['i386', 'x86_64'])) @skipIfRemote @@ -33,29 +57,90 @@ self.build() exe = self.getBuildArtifact("a.out") lldbutil.run_to_name_breakpoint(self, "main", exe_name=exe) - # We start tracing from main + # We will start tracing from main self.runCmd("processor-trace start all") # We check the trace after the for loop self.runCmd("b " + str(line_number('main.cpp', '// Break 1'))) self.runCmd("c") - # We wait a little bit to ensure the processor has send the PT packets to - # the memory - time.sleep(.1) + command = "processor-trace show-instr-log -c 100" - # We find the start address of the 'fun' function for a later check - target = self.dbg.GetSelectedTarget() - fun_start_adddress = target.FindFunctions("fun")[0].GetSymbol() \ - .GetStartAddress().GetLoadAddress(target) + def isTraceAvailable(): + res = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand(command, res) + return "Instruction Log not available" not in res.GetOutput() - # We print the last instructions - self.expect("processor-trace show-instr-log -c 100", + self.waitUntilTrue(isTraceAvailable) + + self.expect(command, patterns=[ # We expect to have seen the first instruction of 'fun' - hex(fun_start_adddress), + hex(self.find_start_address_of_function(self.dbg.GetSelectedTarget(), "fun")), # We expect to see the exit condition of the for loop "at main.cpp:" + str(line_number('main.cpp', '// Break for loop')) ]) self.runCmd("processor-trace stop") + + @skipIf(oslist=no_match(['linux'])) + @skipIf(archs=no_match(['i386', 'x86_64'])) + @skipIfRemote + def test_basic_flow_with_python_api(self): + """Test collection, decoding, and dumping instructions using the Python API""" + + self.build() + exe = self.getBuildArtifact("a.out") + + # We will start tracing from main + lldbutil.run_to_name_breakpoint(self, "main", exe_name=exe) + + decoder = lldbIntelFeatures.PTDecoder(self.dbg) + + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + tid = process.GetProcessID() + + # We configure the tracing options + trace_opts = lldb.SBTraceOptions() + trace_opts.setThreadID(tid) + trace_opts.setType(lldb.eTraceTypeProcessorTrace) + trace_opts.setMetaDataBufferSize(0) + trace_opts.setTraceBufferSize(4096) + + stream = lldb.SBStream() + stream.Print('{"trace-tech":"intel-pt"}') + custom_params = lldb.SBStructuredData() + self.assertSuccess(custom_params.SetFromJSON(stream)) + trace_opts.setTraceParams(custom_params) + + # We start tracing + error = lldb.SBError() + decoder.StartProcessorTrace(process, trace_opts, error) + self.assertSuccess(error) + + # We check the trace after the for loop + self.runCmd("b " + str(line_number('main.cpp', '// Break 1'))) + self.runCmd("c") + + # We'll check repeteadly until the trace is available + def getInstructions(): + instruction_list = lldbIntelFeatures.PTInstructionList() + decoder.GetInstructionLogAtOffset( + process, tid, offset=99, count=100, result_list=instruction_list, sberror=error) + if error.Fail() or instruction_list.GetSize() == 0: + return None + return instruction_list + + instruction_list = self.waitUntilNotNone(getInstructions) + + # We assert we executed the "fun" function in the trace + addresses = set() + for i in range(0, instruction_list.GetSize()): + insn = instruction_list.GetInstructionAtIndex(i) + addresses.add(insn.GetInsnAddress()) + self.assertIn(self.find_start_address_of_function(target, "fun"), addresses) + + # We assert stopping the trace works + decoder.StopProcessorTrace(process, error, tid) + self.assertSuccess(error) diff --git a/lldb/tools/intel-features/CMakeLists.txt b/lldb/tools/intel-features/CMakeLists.txt --- a/lldb/tools/intel-features/CMakeLists.txt +++ b/lldb/tools/intel-features/CMakeLists.txt @@ -62,6 +62,7 @@ # Add link dependencies for python wrapper if (LLDB_ENABLE_PYTHON AND LLDB_BUILD_INTEL_PT) add_dependencies(lldbIntelFeatures intel-features-swig_wrapper) + add_dependencies(lldbIntelFeatures finish-intel-features-swig) endif() install(TARGETS lldbIntelFeatures diff --git a/lldb/tools/intel-features/scripts/CMakeLists.txt b/lldb/tools/intel-features/scripts/CMakeLists.txt --- a/lldb/tools/intel-features/scripts/CMakeLists.txt +++ b/lldb/tools/intel-features/scripts/CMakeLists.txt @@ -35,3 +35,45 @@ add_custom_target(intel-features-swig_wrapper ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/IntelFeaturesPythonWrap.cpp ) + +# Set up the python module in a similar way as in lldb/CmakeLists.txt + +if(LLDB_BUILD_FRAMEWORK) + set(lldb_intel_features_python_build_path + "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}/LLDB.framework/Resources/Python/lldbIntelFeatures") +else() + set(lldb_intel_features_python_build_path + "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${LLDB_PYTHON_RELATIVE_PATH}/lldbIntelFeatures") +endif() + +# Add a Post-Build Event to copy over Python files and create the symlink +# to libIntelFeatures.so for the Python API. +add_custom_target(finish-intel-features-swig ALL VERBATIM + COMMAND ${CMAKE_COMMAND} -E make_directory ${lldb_intel_features_python_build_path} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py + COMMENT "Python script sym-linking LLDB Intel Features Python API") + +add_custom_command(TARGET finish-intel-features-swig POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_BINARY_DIR}/lldbIntelFeatures.py" + "${lldb_intel_features_python_build_path}/__init__.py") + +set(libIntelFeatures_symlink_dest + "${LLVM_SHLIB_OUTPUT_INTDIR}/liblldbIntelFeatures${CMAKE_SHARED_LIBRARY_SUFFIX}") + +function(create_relative_symlink target dest_file output_dir output_name) + get_filename_component(dest_file ${dest_file} ABSOLUTE) + get_filename_component(output_dir ${output_dir} ABSOLUTE) + file(RELATIVE_PATH rel_dest_file ${output_dir} ${dest_file}) + if(CMAKE_HOST_UNIX) + set(LLVM_LINK_OR_COPY create_symlink) + else() + set(LLVM_LINK_OR_COPY copy) + endif() + add_custom_command(TARGET ${target} POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -E ${LLVM_LINK_OR_COPY} ${rel_dest_file} ${output_name} + WORKING_DIRECTORY ${output_dir}) +endfunction() + +create_relative_symlink(finish-intel-features-swig ${libIntelFeatures_symlink_dest} + ${lldb_intel_features_python_build_path} _lldbIntelFeatures.so) diff --git a/lldb/tools/intel-features/scripts/lldb-intel-features.swig b/lldb/tools/intel-features/scripts/lldb-intel-features.swig --- a/lldb/tools/intel-features/scripts/lldb-intel-features.swig +++ b/lldb/tools/intel-features/scripts/lldb-intel-features.swig @@ -1,4 +1,21 @@ -%module lldbIntelFeatures +/* +Swig import fix copied from lldb/bindings/python.swig +*/ + +%define MODULEIMPORT +"try: + # Try an absolute import first. + import $module +except ImportError: + # Relative import should work if we are being loaded by Python. + from . import $module" +%enddef +// These versions will not generate working python modules, so error out early. +#if SWIG_VERSION >= 0x030009 && SWIG_VERSION < 0x030011 +#error Swig versions 3.0.9 and 3.0.10 are incompatible with lldb. +#endif + +%module(moduleimport=MODULEIMPORT) lldbIntelFeatures %{ #include "lldb/lldb-public.h"