wallace created this revision. wallace added reviewers: clayborg, labath. Herald added subscribers: lldb-commits, mgorny. Herald added a project: LLDB. wallace requested review of this revision.
The initial implementation of the Intel PT support for LLDB came with some Python support. However, when LLDB was upgraded to more modern versions of SWIG and Python3, the Intel Pt Python API broke and has not been updated since then. This makes me believe that no one has used that API in a looong time. As a fix, I replicated some fixes done in the core LLDB Python support. Additionally, I added a test that uses entirely the Intel PT Python API and made some additional fixes to the existing test. I'm not fond of the existing API design, so I'll be improving it in following diffs. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D84791 Files: lldb/source/API/SBDebugger.cpp lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py lldb/tools/intel-features/CMakeLists.txt lldb/tools/intel-features/scripts/CMakeLists.txt lldb/tools/intel-features/scripts/lldb-intel-features.swig
Index: lldb/tools/intel-features/scripts/lldb-intel-features.swig =================================================================== --- lldb/tools/intel-features/scripts/lldb-intel-features.swig +++ lldb/tools/intel-features/scripts/lldb-intel-features.swig @@ -1,4 +1,22 @@ -%module lldbIntelFeatures +/* +Swig import fix copied from lldb/bindings/python.swig +*/ + +%define MODULEIMPORT +"try: + # Try an absolute import first. If we're being loaded from lldb, + # _lldb should be a built-in module. + 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" Index: lldb/tools/intel-features/scripts/CMakeLists.txt =================================================================== --- lldb/tools/intel-features/scripts/CMakeLists.txt +++ 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) Index: lldb/tools/intel-features/CMakeLists.txt =================================================================== --- lldb/tools/intel-features/CMakeLists.txt +++ 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 Index: lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py =================================================================== --- lldb/test/API/tools/intel-features/intel-pt/test/TestIntelPTSimpleBinary.py +++ 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) Index: lldb/source/API/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ 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"); }
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits