summaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/bindings/interface/SBProgressDocstrings.i3
-rw-r--r--lldb/bindings/interface/SBStructuredDataExtensions.i111
-rw-r--r--lldb/bindings/python/python.swig17
-rw-r--r--lldb/docs/dil-expr-lang.ebnf6
-rw-r--r--lldb/docs/use/remote.rst18
-rw-r--r--lldb/examples/python/cmdtemplate.py10
-rw-r--r--lldb/examples/python/templates/parsed_cmd.py57
-rw-r--r--lldb/examples/python/templates/scripted_process.py136
-rw-r--r--lldb/include/lldb/API/SBFrame.h2
-rw-r--r--lldb/include/lldb/API/SBFunction.h2
-rw-r--r--lldb/include/lldb/API/SBSymbol.h2
-rw-r--r--lldb/include/lldb/API/SBSymbolContext.h1
-rw-r--r--lldb/include/lldb/Core/Architecture.h9
-rw-r--r--lldb/include/lldb/Core/Disassembler.h38
-rw-r--r--lldb/include/lldb/Core/FormatEntity.h1
-rw-r--r--lldb/include/lldb/Core/Mangled.h11
-rw-r--r--lldb/include/lldb/Core/Opcode.h2
-rw-r--r--lldb/include/lldb/Core/ProtocolServer.h2
-rw-r--r--lldb/include/lldb/Expression/DWARFExpression.h3
-rw-r--r--lldb/include/lldb/Expression/Expression.h8
-rw-r--r--lldb/include/lldb/Expression/IRMemoryMap.h2
-rw-r--r--lldb/include/lldb/Host/File.h16
-rw-r--r--lldb/include/lldb/Host/HostInfoBase.h14
-rw-r--r--lldb/include/lldb/Host/JSONTransport.h11
-rw-r--r--lldb/include/lldb/Host/macosx/HostInfoMacOSX.h3
-rw-r--r--lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h55
-rw-r--r--lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h10
-rw-r--r--lldb/include/lldb/Interpreter/Options.h4
-rw-r--r--lldb/include/lldb/Interpreter/ScriptInterpreter.h5
-rw-r--r--lldb/include/lldb/Protocol/MCP/Protocol.h240
-rw-r--r--lldb/include/lldb/Protocol/MCP/Resource.h2
-rw-r--r--lldb/include/lldb/Protocol/MCP/Server.h82
-rw-r--r--lldb/include/lldb/Protocol/MCP/Tool.h3
-rw-r--r--lldb/include/lldb/Protocol/MCP/Transport.h48
-rw-r--r--lldb/include/lldb/Symbol/SymbolFile.h17
-rw-r--r--lldb/include/lldb/Target/CoreFileMemoryRanges.h2
-rw-r--r--lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h3
-rw-r--r--lldb/include/lldb/Target/RegisterContextUnwind.h45
-rw-r--r--lldb/include/lldb/Target/StackFrame.h51
-rw-r--r--lldb/include/lldb/Target/StackFrameList.h12
-rw-r--r--lldb/include/lldb/Target/StackID.h50
-rw-r--r--lldb/include/lldb/Target/Statistics.h22
-rw-r--r--lldb/include/lldb/Target/StopInfo.h6
-rw-r--r--lldb/include/lldb/Target/Thread.h5
-rw-r--r--lldb/include/lldb/Target/UnwindLLDB.h2
-rw-r--r--lldb/include/lldb/Utility/AnsiTerminal.h5
-rw-r--r--lldb/include/lldb/ValueObject/DILAST.h52
-rw-r--r--lldb/include/lldb/ValueObject/DILEval.h9
-rw-r--r--lldb/include/lldb/ValueObject/DILLexer.h4
-rw-r--r--lldb/include/lldb/ValueObject/DILParser.h3
-rw-r--r--lldb/include/lldb/lldb-forward.h3
-rw-r--r--lldb/packages/Python/lldbsuite/test/configuration.py11
-rw-r--r--lldb/packages/Python/lldbsuite/test/decorators.py4
-rw-r--r--lldb/packages/Python/lldbsuite/test/dotest.py3
-rw-r--r--lldb/packages/Python/lldbsuite/test/lldbtest.py27
-rw-r--r--lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h27
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py16
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py35
-rw-r--r--lldb/source/API/SBCommandInterpreter.cpp2
-rw-r--r--lldb/source/API/SBFrame.cpp16
-rw-r--r--lldb/source/API/SBFunction.cpp9
-rw-r--r--lldb/source/API/SBHostOS.cpp10
-rw-r--r--lldb/source/API/SBSymbol.cpp9
-rw-r--r--lldb/source/API/SBThread.cpp107
-rw-r--r--lldb/source/Commands/CommandObjectBreakpoint.cpp36
-rw-r--r--lldb/source/Commands/CommandObjectCommands.cpp4
-rw-r--r--lldb/source/Commands/CommandObjectDisassemble.cpp11
-rw-r--r--lldb/source/Commands/CommandObjectDisassemble.h1
-rw-r--r--lldb/source/Commands/CommandObjectFrame.cpp11
-rw-r--r--lldb/source/Commands/CommandObjectLog.cpp2
-rw-r--r--lldb/source/Commands/CommandObjectMemory.cpp7
-rw-r--r--lldb/source/Commands/CommandObjectMultiword.cpp2
-rw-r--r--lldb/source/Commands/CommandObjectProcess.cpp2
-rw-r--r--lldb/source/Commands/CommandObjectProtocolServer.cpp15
-rw-r--r--lldb/source/Commands/CommandObjectSource.cpp6
-rw-r--r--lldb/source/Commands/CommandObjectTarget.cpp9
-rw-r--r--lldb/source/Commands/CommandObjectThread.cpp2
-rw-r--r--lldb/source/Commands/CommandObjectWatchpoint.cpp24
-rw-r--r--lldb/source/Commands/CommandObjectWatchpointCommand.cpp6
-rw-r--r--lldb/source/Commands/Options.td3240
-rw-r--r--lldb/source/Core/CoreProperties.td4
-rw-r--r--lldb/source/Core/Disassembler.cpp156
-rw-r--r--lldb/source/Core/FormatEntity.cpp16
-rw-r--r--lldb/source/Core/Mangled.cpp15
-rw-r--r--lldb/source/Core/ModuleList.cpp15
-rw-r--r--lldb/source/Core/ProtocolServer.cpp34
-rw-r--r--lldb/source/Expression/DWARFExpression.cpp10
-rw-r--r--lldb/source/Expression/DWARFExpressionList.cpp2
-rw-r--r--lldb/source/Expression/Expression.cpp29
-rw-r--r--lldb/source/Expression/IRExecutionUnit.cpp59
-rw-r--r--lldb/source/Expression/IRMemoryMap.cpp13
-rw-r--r--lldb/source/Host/common/Editline.cpp30
-rw-r--r--lldb/source/Host/common/File.cpp34
-rw-r--r--lldb/source/Host/common/HostInfoBase.cpp38
-rw-r--r--lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm12
-rw-r--r--lldb/source/Host/windows/Host.cpp46
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp37
-rw-r--r--lldb/source/Interpreter/CommandObject.cpp12
-rw-r--r--lldb/source/Interpreter/Options.cpp38
-rw-r--r--lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp7
-rw-r--r--lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp186
-rw-r--r--lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h5
-rw-r--r--lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp12
-rw-r--r--lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h3
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp22
-rw-r--r--lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp19
-rw-r--r--lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp8
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp372
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h70
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp24
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp67
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxx.h17
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp29
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp59
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp22
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp10
-rw-r--r--lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp41
-rw-r--r--lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp35
-rw-r--r--lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h3
-rw-r--r--lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp122
-rw-r--r--lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp329
-rw-r--r--lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h13
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp557
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h53
-rw-r--r--lldb/source/Plugins/Process/Utility/CMakeLists.txt1
-rw-r--r--lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp11
-rw-r--r--lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h11
-rw-r--r--lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp117
-rw-r--r--lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h42
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp2
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h14
-rw-r--r--lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp46
-rw-r--r--lldb/source/Plugins/Process/elf-core/ProcessElfCore.h14
-rw-r--r--lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp27
-rw-r--r--lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h1
-rw-r--r--lldb/source/Plugins/Process/elf-core/RegisterUtilities.h5
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp2
-rw-r--r--lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp17
-rw-r--r--lldb/source/Plugins/Process/scripted/CMakeLists.txt1
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp191
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedFrame.h63
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedThread.cpp82
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedThread.h5
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp129
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h24
-rw-r--r--lldb/source/Plugins/Protocol/MCP/Resource.cpp15
-rw-r--r--lldb/source/Plugins/Protocol/MCP/Resource.h15
-rw-r--r--lldb/source/Plugins/Protocol/MCP/Tool.cpp12
-rw-r--r--lldb/source/Plugins/Protocol/MCP/Tool.h8
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt1
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h1
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp157
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h59
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp3
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp17
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h5
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp5
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h2
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp18
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp185
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h15
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp4
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h2
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp1
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp110
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h8
-rw-r--r--lldb/source/Protocol/MCP/CMakeLists.txt2
-rw-r--r--lldb/source/Protocol/MCP/Protocol.cpp203
-rw-r--r--lldb/source/Protocol/MCP/Server.cpp291
-rw-r--r--lldb/source/Protocol/MCP/Transport.cpp23
-rw-r--r--lldb/source/Symbol/ObjectFile.cpp2
-rw-r--r--lldb/source/Target/ABI.cpp4
-rw-r--r--lldb/source/Target/InstrumentationRuntimeStopInfo.cpp42
-rw-r--r--lldb/source/Target/Process.cpp8
-rw-r--r--lldb/source/Target/RegisterContextUnwind.cpp44
-rw-r--r--lldb/source/Target/StackFrame.cpp40
-rw-r--r--lldb/source/Target/StackFrameList.cpp8
-rw-r--r--lldb/source/Target/StackID.cpp11
-rw-r--r--lldb/source/Target/Statistics.cpp20
-rw-r--r--lldb/source/Target/StopInfo.cpp105
-rw-r--r--lldb/source/Utility/ArchSpec.cpp163
-rw-r--r--lldb/source/ValueObject/DILAST.cpp9
-rw-r--r--lldb/source/ValueObject/DILEval.cpp254
-rw-r--r--lldb/source/ValueObject/DILLexer.cpp57
-rw-r--r--lldb/source/ValueObject/DILParser.cpp67
-rw-r--r--lldb/test/API/commands/command/script/add/TestAddParsedCommand.py16
-rw-r--r--lldb/test/API/commands/command/script/add/test_commands.py16
-rw-r--r--lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py1
-rw-r--r--lldb/test/API/commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py1
-rw-r--r--lldb/test/API/commands/frame/select/TestFrameSelect.py6
-rw-r--r--lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py29
-rw-r--r--lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp9
-rw-r--r--lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py33
-rw-r--r--lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py2
-rw-r--r--lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile3
-rw-r--r--lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py88
-rw-r--r--lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp3
-rw-r--r--lldb/test/API/commands/statistics/basic/TestStats.py100
-rw-r--r--lldb/test/API/commands/statistics/basic/dwo_error_foo.cpp10
-rw-r--r--lldb/test/API/commands/statistics/basic/dwo_error_main.cpp5
-rw-r--r--lldb/test/API/functionalities/asan/TestMemoryHistory.py12
-rw-r--r--lldb/test/API/functionalities/asan/TestReportData.py7
-rw-r--r--lldb/test/API/functionalities/completion/TestCompletion.py12
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ranges/ref_view/TestDataFormatterStdRangesRefView.py1
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py6
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/main.cpp35
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py10
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp2
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py2
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/main.cpp6
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py2
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp3
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/Makefile32
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py118
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/d_original_example.s461
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/live_across_call.s371
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s557
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s304
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/regs_int_params.s312
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s375
-rw-r--r--lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s289
-rw-r--r--lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py2
-rw-r--r--lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py2
-rw-r--r--lldb/test/API/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferiorStep.py2
-rw-r--r--lldb/test/API/functionalities/multiword-commands/TestMultiWordCommands.py2
-rw-r--r--lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py37
-rw-r--r--lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py2
-rw-r--r--lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.c24
-rw-r--r--lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.corebin0 -> 24576 bytes
-rw-r--r--lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py2
-rw-r--r--lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py5
-rw-r--r--lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py2
-rw-r--r--lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py65
-rw-r--r--lldb/test/API/functionalities/statusline/TestStatusline.py1
-rw-r--r--lldb/test/API/functionalities/thread/step_until/TestStepUntil.py10
-rw-r--r--lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py50
-rw-r--r--lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py7
-rw-r--r--lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py7
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile1
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py73
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml64
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json41
-rw-r--r--lldb/test/API/lang/cpp/abi_tag_structors/Makefile3
-rw-r--r--lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py116
-rw-r--r--lldb/test/API/lang/cpp/abi_tag_structors/main.cpp62
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py76
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp6
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h11
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp15
-rw-r--r--lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py3
-rw-r--r--lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py2
-rw-r--r--lldb/test/API/macosx/arm-corefile-regctx/Makefile6
-rw-r--r--lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py17
-rw-r--r--lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml30
-rw-r--r--lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml36
-rw-r--r--lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp266
-rw-r--r--lldb/test/API/macosx/arm-pointer-metadata-stripping/Makefile2
-rw-r--r--lldb/test/API/macosx/arm-pointer-metadata-stripping/TestArmPointerMetadataStripping.py48
-rw-r--r--lldb/test/API/macosx/arm-pointer-metadata-stripping/extra_symbols.json21
-rw-r--r--lldb/test/API/macosx/arm-pointer-metadata-stripping/main.c13
-rw-r--r--lldb/test/API/macosx/riscv32-corefile/Makefile7
-rw-r--r--lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py17
-rw-r--r--lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp116
-rw-r--r--lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml46
-rw-r--r--lldb/test/API/python_api/basename/Makefile3
-rw-r--r--lldb/test/API/python_api/basename/TestGetBaseName.py40
-rw-r--r--lldb/test/API/python_api/basename/main.cpp16
-rw-r--r--lldb/test/API/python_api/interpreter/TestRunCommandInterpreterAPI.py2
-rw-r--r--lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py105
-rw-r--r--lldb/test/API/riscv/step/TestSoftwareStep.py35
-rw-r--r--lldb/test/API/riscv/step/branch.c1
-rw-r--r--lldb/test/API/riscv/step/incomplete_sequence_without_lr.c2
-rw-r--r--lldb/test/API/riscv/step/incomplete_sequence_without_sc.c2
-rw-r--r--lldb/test/API/riscv/step/main.c5
-rw-r--r--lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py1
-rw-r--r--lldb/test/API/tools/lldb-dap/console/TestDAP_console.py1
-rw-r--r--lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py42
-rw-r--r--lldb/test/API/tools/lldb-dap/server/TestDAP_server.py59
-rw-r--r--lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py1
-rw-r--r--lldb/test/API/tools/lldb-dap/step/TestDAP_step.py1
-rw-r--r--lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py1
-rw-r--r--lldb/test/API/windows/launch/replace-dll/Makefile1
-rw-r--r--lldb/test/API/windows/launch/replace-dll/TestReplaceDLL.py62
-rw-r--r--lldb/test/API/windows/launch/replace-dll/bar.c1
-rw-r--r--lldb/test/API/windows/launch/replace-dll/foo.c1
-rw-r--r--lldb/test/API/windows/launch/replace-dll/test.c3
-rw-r--r--lldb/test/CMakeLists.txt1
-rw-r--r--lldb/test/Shell/Host/TestCustomShell.test2
-rw-r--r--lldb/test/Shell/Process/Optimization.test2
-rw-r--r--lldb/test/Shell/Process/UnsupportedLanguage.test5
-rw-r--r--lldb/test/Shell/Recognizer/ubsan_add_overflow.test4
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test32
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test30
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test30
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test30
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test32
-rw-r--r--lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test30
-rw-r--r--lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test5
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/deterministic-build.cpp5
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/dwo-missing-error.test4
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/dwo-static-data-member-access.test3
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test70
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp5
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp45
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test109
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/expressions.test3
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/variables.test34
-rw-r--r--lldb/test/Shell/SymbolFile/add-dsym.test2
-rw-r--r--lldb/test/Shell/Symtab/symtab-wasm.test25
-rw-r--r--lldb/test/Shell/lit.cfg.py10
-rw-r--r--lldb/tools/CMakeLists.txt3
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.mm11
-rw-r--r--lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp81
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp12
-rw-r--r--lldb/tools/driver/Options.td417
-rw-r--r--lldb/tools/lldb-dap/DAP.cpp10
-rw-r--r--lldb/tools/lldb-dap/DAP.h10
-rw-r--r--lldb/tools/lldb-dap/EventHelper.cpp10
-rw-r--r--lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp7
-rw-r--r--lldb/tools/lldb-dap/Options.td15
-rw-r--r--lldb/tools/lldb-dap/Protocol/DAPTypes.h15
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp2
-rw-r--r--lldb/tools/lldb-dap/README.md19
-rw-r--r--lldb/tools/lldb-dap/package.json30
-rw-r--r--lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts89
-rw-r--r--lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts5
-rw-r--r--lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts41
-rw-r--r--lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts10
-rw-r--r--lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts20
-rw-r--r--lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts1
-rw-r--r--lldb/tools/lldb-dap/tool/lldb-dap.cpp93
-rw-r--r--lldb/tools/lldb-mcp/CMakeLists.txt34
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp-Info.plist.in21
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp.cpp176
-rw-r--r--lldb/tools/yaml2macho-core/CMakeLists.txt14
-rw-r--r--lldb/tools/yaml2macho-core/CoreSpec.cpp165
-rw-r--r--lldb/tools/yaml2macho-core/CoreSpec.h80
-rw-r--r--lldb/tools/yaml2macho-core/LCNoteWriter.cpp97
-rw-r--r--lldb/tools/yaml2macho-core/LCNoteWriter.h33
-rw-r--r--lldb/tools/yaml2macho-core/MemoryWriter.cpp56
-rw-r--r--lldb/tools/yaml2macho-core/MemoryWriter.h26
-rw-r--r--lldb/tools/yaml2macho-core/ThreadWriter.cpp200
-rw-r--r--lldb/tools/yaml2macho-core/ThreadWriter.h24
-rw-r--r--lldb/tools/yaml2macho-core/Utility.cpp22
-rw-r--r--lldb/tools/yaml2macho-core/Utility.h18
-rw-r--r--lldb/tools/yaml2macho-core/yaml2macho.cpp250
-rw-r--r--lldb/unittests/API/SBCommandInterpreterTest.cpp4
-rw-r--r--lldb/unittests/CMakeLists.txt4
-rw-r--r--lldb/unittests/Core/FormatEntityTest.cpp1
-rw-r--r--lldb/unittests/DAP/DAPTest.cpp1
-rw-r--r--lldb/unittests/DAP/TestBase.cpp1
-rw-r--r--lldb/unittests/Editline/EditlineTest.cpp3
-rw-r--r--lldb/unittests/Expression/ExpressionTest.cpp37
-rw-r--r--lldb/unittests/Language/CPlusPlus/CMakeLists.txt1
-rw-r--r--lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp175
-rw-r--r--lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp81
-rw-r--r--lldb/unittests/Protocol/CMakeLists.txt1
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPServerTest.cpp292
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPTest.cpp64
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPTestUtilities.h40
-rw-r--r--lldb/unittests/ProtocolServer/CMakeLists.txt11
-rw-r--r--lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp333
-rw-r--r--lldb/unittests/Symbol/TestTypeSystemClang.cpp14
-rw-r--r--lldb/unittests/ValueObject/DILLexerTests.cpp42
-rw-r--r--lldb/utils/TableGen/LLDBOptionDefEmitter.cpp29
379 files changed, 14194 insertions, 4899 deletions
diff --git a/lldb/bindings/interface/SBProgressDocstrings.i b/lldb/bindings/interface/SBProgressDocstrings.i
index 4c001d7d5ebc..218d0c973cc4 100644
--- a/lldb/bindings/interface/SBProgressDocstrings.i
+++ b/lldb/bindings/interface/SBProgressDocstrings.i
@@ -57,8 +57,9 @@ Additionally for Python, progress is supported in a with statement. ::
with lldb.SBProgress('Non deterministic progress', 'Detail', lldb.SBDebugger) as progress:
for i in range(10):
progress.Increment(1)
- # The progress object is automatically finalized when the with statement
+ ...
+The progress object is automatically finalized on the exit of the with block.
") lldb::SBProgress;
%feature("docstring",
diff --git a/lldb/bindings/interface/SBStructuredDataExtensions.i b/lldb/bindings/interface/SBStructuredDataExtensions.i
index ca3d0966f9fc..af76cfc5c2db 100644
--- a/lldb/bindings/interface/SBStructuredDataExtensions.i
+++ b/lldb/bindings/interface/SBStructuredDataExtensions.i
@@ -3,16 +3,119 @@ STRING_EXTENSION_OUTSIDE(SBStructuredData)
%extend lldb::SBStructuredData {
#ifdef SWIGPYTHON
%pythoncode%{
- def __int__(self):
- return self.GetSignedInteger()
-
def __len__(self):
'''Return the number of element in a lldb.SBStructuredData object.'''
return self.GetSize()
def __iter__(self):
'''Iterate over all the elements in a lldb.SBStructuredData object.'''
- return lldb_iter(self, 'GetSize', 'GetItemAtIndex')
+ data_type = self.GetType()
+ if data_type == eStructuredDataTypeArray:
+ for i in range(self.GetSize()):
+ yield self.GetItemAtIndex(i).dynamic
+ return
+ elif data_type == eStructuredDataTypeDictionary:
+ keys = SBStringList()
+ self.GetKeys(keys)
+ return iter(keys)
+ else:
+ raise TypeError(f"cannot iterate {self.type_name(data_type)} type")
+
+ def __getitem__(self, key):
+ data_type = self.GetType()
+ if data_type == eStructuredDataTypeArray:
+ if not isinstance(key, int):
+ raise TypeError("subscript index must be an integer")
+ count = len(self)
+ if -count <= key < count:
+ key %= count
+ return self.GetItemAtIndex(key).dynamic
+ raise IndexError("index out of range")
+ elif data_type == eStructuredDataTypeDictionary:
+ if not isinstance(key, str):
+ raise TypeError("subscript key must be a string")
+ return self.GetValueForKey(key).dynamic
+ else:
+ raise TypeError(f"cannot subscript {self.type_name(data_type)} type")
+
+ def __str__(self):
+ data_type = self.GetType()
+ if data_type in (
+ eStructuredDataTypeString,
+ eStructuredDataTypeInteger,
+ eStructuredDataTypeSignedInteger,
+ eStructuredDataTypeFloat,
+ ):
+ return str(self.dynamic)
+ else:
+ return repr(self)
+
+ def __int__(self):
+ data_type = self.GetType()
+ if data_type in (
+ eStructuredDataTypeInteger,
+ eStructuredDataTypeSignedInteger,
+ ):
+ return self.dynamic
+ else:
+ raise TypeError(f"cannot convert {self.type_name(data_type)} to int")
+
+ def __float__(self):
+ data_type = self.GetType()
+ if data_type in (
+ eStructuredDataTypeFloat,
+ eStructuredDataTypeInteger,
+ eStructuredDataTypeSignedInteger,
+ ):
+ return float(self.dynamic)
+ else:
+ raise TypeError(f"cannot convert {self.type_name(data_type)} to float")
+
+ @property
+ def dynamic(self):
+ data_type = self.GetType()
+ if data_type == eStructuredDataTypeNull:
+ return None
+ elif data_type == eStructuredDataTypeBoolean:
+ return self.GetBooleanValue()
+ elif data_type == eStructuredDataTypeInteger:
+ return self.GetUnsignedIntegerValue()
+ elif data_type == eStructuredDataTypeSignedInteger:
+ return self.GetSignedIntegerValue()
+ elif data_type == eStructuredDataTypeFloat:
+ return self.GetFloatValue()
+ elif data_type == eStructuredDataTypeString:
+ size = len(self) or 1023
+ return self.GetStringValue(size + 1)
+ elif data_type == eStructuredDataTypeGeneric:
+ return self.GetGenericValue()
+ else:
+ return self
+
+ @staticmethod
+ def type_name(t):
+ if t == eStructuredDataTypeNull:
+ return "null"
+ elif t == eStructuredDataTypeBoolean:
+ return "boolean"
+ elif t == eStructuredDataTypeInteger:
+ return "integer"
+ elif t == eStructuredDataTypeSignedInteger:
+ return "integer"
+ elif t == eStructuredDataTypeFloat:
+ return "float"
+ elif t == eStructuredDataTypeString:
+ return "string"
+ elif t == eStructuredDataTypeArray:
+ return "array"
+ elif t == eStructuredDataTypeDictionary:
+ return "dictionary"
+ elif t == eStructuredDataTypeGeneric:
+ return "generic"
+ elif t == eStructuredDataTypeInvalid:
+ return "invalid"
+ else:
+ raise TypeError(f"unknown structured data type: {t}")
%}
#endif
}
diff --git a/lldb/bindings/python/python.swig b/lldb/bindings/python/python.swig
index 278c0eed2bab..4a5a39dc4b06 100644
--- a/lldb/bindings/python/python.swig
+++ b/lldb/bindings/python/python.swig
@@ -59,23 +59,6 @@ except ImportError:
// Parameter types will be used in the autodoc string.
%feature("autodoc", "1");
-%define ARRAYHELPER(type,name)
-%inline %{
-type *new_ ## name (int nitems) {
- return (type *) malloc(sizeof(type)*nitems);
-}
-void delete_ ## name(type *t) {
- free(t);
-}
-type name ## _get(type *t, int index) {
- return t[index];
-}
-void name ## _set(type *t, int index, type val) {
- t[index] = val;
-}
-%}
-%enddef
-
%pythoncode%{
import uuid
import re
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 783432dabd6d..67328939ba42 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -15,7 +15,8 @@ postfix_expression = primary_expression
| postfix_expression "." id_expression
| postfix_expression "->" id_expression ;
-primary_expression = id_expression
+primary_expression = numeric_literal
+ | id_expression
| "(" expression ")" ;
id_expression = unqualified_id
@@ -31,6 +32,9 @@ identifier = ? C99 Identifier ? ;
integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ;
+numeric_literal = ? Integer constant: hexademical, decimal, octal, binary ?
+ | ? Floating constant ? ;
+
register = "$" ? Register name ? ;
nested_name_specifier = type_name "::"
diff --git a/lldb/docs/use/remote.rst b/lldb/docs/use/remote.rst
index e2009ccc65f7..05b59a97d9d3 100644
--- a/lldb/docs/use/remote.rst
+++ b/lldb/docs/use/remote.rst
@@ -36,13 +36,13 @@ Remote system
*************
On Linux and Android, all required remote functionality is contained in the
-lldb-server binary. This binary combines the functionality of the platform and
+``lldb-server`` binary. This binary combines the functionality of the platform and
gdb-remote stub. A single binary facilitates deployment and reduces code size,
-since the two functions share a lot of code. The lldb-server binary is also
-statically linked with the rest of LLDB (unlike lldb, which dynamically links
-to liblldb.so by default), so it does not have any dependencies on the rest of
+since the two functions share a lot of code. The ``lldb-server`` binary is also
+statically linked with the rest of LLDB (unlike ``lldb``, which dynamically links
+to ``liblldb.so`` by default), so it does not have any dependencies on the rest of
lldb. On macOS and iOS, the remote-gdb functionality is implemented by the
-debugserver binary, which you will need to deploy alongside lldb-server.
+``debugserver`` binary, which you will need to deploy alongside ``lldb-server``.
The binaries mentioned above need to be present on the remote system to enable
remote debugging. You can either compile on the remote system directly or copy
@@ -51,8 +51,8 @@ differs from the local one, you will need to cross-compile the correct version
of the binaries. More information on cross-compiling LLDB can be found on the
build page.
-Once the binaries are in place, you just need to run the lldb-server in
-platform mode and specify the port it should listen on. For example, the
+Once the binaries are in place, you just need to run the ``lldb-server`` in
+``platform`` mode and specify the port it should listen on. For example, the
command
::
@@ -60,8 +60,8 @@ command
remote% lldb-server platform --listen "*:1234" --server
will start the LLDB platform and wait for incoming connections from any address
-to port 1234. Specifying an address instead of * will only allow connections
-originating from that address. Adding a --server parameter to the command line
+to port ``1234``. Specifying an address instead of ``*`` will only allow connections
+originating from that address. Adding a ``--server`` parameter to the command line
will fork off a new process for every incoming connection, allowing multiple
parallel debug sessions.
diff --git a/lldb/examples/python/cmdtemplate.py b/lldb/examples/python/cmdtemplate.py
index a9fbe0b40e19..be4c2da53ee0 100644
--- a/lldb/examples/python/cmdtemplate.py
+++ b/lldb/examples/python/cmdtemplate.py
@@ -74,6 +74,11 @@ class FrameStatCommand(ParsedCommand):
dest = "statics",
default = True,
)
+ ov_parser.add_option(
+ "t",
+ "test-flag",
+ help="test a flag value.",
+ )
def get_repeat_command(self, args):
"""As an example, make the command not auto-repeat:"""
@@ -123,6 +128,11 @@ class FrameStatCommand(ParsedCommand):
% (variables_count, total_size, average_size),
file=result,
)
+ if ov_parser.was_set("test-flag"):
+ print("Got the test flag")
+ else:
+ print("Got no test flag")
+
# not returning anything is akin to returning success
diff --git a/lldb/examples/python/templates/parsed_cmd.py b/lldb/examples/python/templates/parsed_cmd.py
index 13d6eae405c0..c1baad3ffd24 100644
--- a/lldb/examples/python/templates/parsed_cmd.py
+++ b/lldb/examples/python/templates/parsed_cmd.py
@@ -241,12 +241,18 @@ class LLDBOptionValueParser:
starts, you can override this to handle your special option. """
for key, elem in self.options_dict.items():
elem['_value_set'] = False
+ # If there's no value_type, then there can't be a dest.
+ if not "value_type" in elem:
+ continue
+
try:
object.__setattr__(self, elem["dest"], elem["default"])
except AttributeError:
# It isn't an error not to have a "dest" variable name, you'll
# just have to manage this option's value on your own.
continue
+ except KeyError:
+ continue
def set_enum_value(self, enum_values, input):
""" This sets the value for an enum option, you should not have to call this
@@ -271,7 +277,13 @@ class LLDBOptionValueParser:
elem = self.get_option_element(opt_name)
if not elem:
return False
-
+
+ # If there's no value_type in element, then it has no value, so just mark
+ # it set and return:
+ if not "value_type" in elem:
+ elem["_value_set"] = True
+ return True
+
if "enum_values" in elem:
(value, error) = self.set_enum_value(elem["enum_values"], opt_value)
else:
@@ -312,10 +324,19 @@ class LLDBOptionValueParser:
value = self.__dict__[elem["dest"]]
return value
- def add_option(self, short_option, long_option, help, default,
- dest = None, required=False, groups = None,
- value_type=lldb.eArgTypeNone, completion_type=None,
- enum_values=None):
+ def add_option(
+ self,
+ short_option,
+ long_option,
+ help,
+ default=None,
+ dest=None,
+ required=False,
+ groups=None,
+ value_type=None,
+ completion_type=None,
+ enum_values=None,
+ ):
"""
short_option: one character, must be unique, not required
long_option: no spaces, must be unique, required
@@ -340,17 +361,27 @@ class LLDBOptionValueParser:
if not completion_type:
completion_type = self.determine_completion(value_type)
-
- dict = {"short_option" : short_option,
- "required" : required,
- "help" : help,
- "value_type" : value_type,
- "completion_type" : completion_type,
- "dest" : dest,
- "default" : default}
+
+ dict = {
+ "short_option": short_option,
+ "required": required,
+ "help": help,
+ }
if enum_values:
+ if not value_type:
+ print("I am setting value type for an enum value")
+ value_type = lldb.eArgTypeNone
+ else:
+ print(f"An enum value had a type: {value_type}")
dict["enum_values"] = enum_values
+
+ if value_type:
+ dict["value_type"] = value_type
+ dict["completion_type"] = completion_type
+ dict["dest"] = dest
+ dict["default"] = default
+
if groups:
dict["groups"] = groups
diff --git a/lldb/examples/python/templates/scripted_process.py b/lldb/examples/python/templates/scripted_process.py
index b6360b851907..49059d533f38 100644
--- a/lldb/examples/python/templates/scripted_process.py
+++ b/lldb/examples/python/templates/scripted_process.py
@@ -383,6 +383,142 @@ class ScriptedThread(metaclass=ABCMeta):
"""
return self.extended_info
+ def get_scripted_frame_plugin(self):
+ """Get scripted frame plugin name.
+
+ Returns:
+ str: Name of the scripted frame plugin.
+ """
+ return None
+
+
+class ScriptedFrame(metaclass=ABCMeta):
+ """
+ The base class for a scripted frame.
+
+ Most of the base class methods are `@abstractmethod` that need to be
+ overwritten by the inheriting class.
+ """
+
+ @abstractmethod
+ def __init__(self, thread, args):
+ """Construct a scripted frame.
+
+ Args:
+ thread (ScriptedThread): The thread owning this frame.
+ args (lldb.SBStructuredData): A Dictionary holding arbitrary
+ key/value pairs used by the scripted frame.
+ """
+ self.target = None
+ self.originating_thread = None
+ self.thread = None
+ self.args = None
+ self.id = None
+ self.name = None
+ self.register_info = None
+ self.register_ctx = {}
+ self.variables = []
+
+ if (
+ isinstance(thread, ScriptedThread)
+ or isinstance(thread, lldb.SBThread)
+ and thread.IsValid()
+ ):
+ self.target = thread.target
+ self.process = thread.process
+ self.originating_thread = thread
+ self.thread = self.process.GetThreadByIndexID(thread.tid)
+ self.get_register_info()
+
+ @abstractmethod
+ def get_id(self):
+ """Get the scripted frame identifier.
+
+ Returns:
+ int: The identifier of the scripted frame in the scripted thread.
+ """
+ pass
+
+ def get_pc(self):
+ """Get the scripted frame address.
+
+ Returns:
+ int: The optional address of the scripted frame in the scripted thread.
+ """
+ return None
+
+ def get_symbol_context(self):
+ """Get the scripted frame symbol context.
+
+ Returns:
+ lldb.SBSymbolContext: The symbol context of the scripted frame in the scripted thread.
+ """
+ return None
+
+ def is_inlined(self):
+ """Check if the scripted frame is inlined.
+
+ Returns:
+ bool: True if scripted frame is inlined. False otherwise.
+ """
+ return False
+
+ def is_artificial(self):
+ """Check if the scripted frame is artificial.
+
+ Returns:
+ bool: True if scripted frame is artificial. False otherwise.
+ """
+ return True
+
+ def is_hidden(self):
+ """Check if the scripted frame is hidden.
+
+ Returns:
+ bool: True if scripted frame is hidden. False otherwise.
+ """
+ return False
+
+ def get_function_name(self):
+ """Get the scripted frame function name.
+
+ Returns:
+ str: The function name of the scripted frame.
+ """
+ return self.name
+
+ def get_display_function_name(self):
+ """Get the scripted frame display function name.
+
+ Returns:
+ str: The display function name of the scripted frame.
+ """
+ return self.get_function_name()
+
+ def get_variables(self, filters):
+ """Get the scripted thread state type.
+
+ Args:
+ filter (lldb.SBVariablesOptions): The filter used to resolve the variables
+ Returns:
+ lldb.SBValueList: The SBValueList containing the SBValue for each resolved variable.
+ Returns None by default.
+ """
+ return None
+
+ def get_register_info(self):
+ if self.register_info is None:
+ self.register_info = self.originating_thread.get_register_info()
+ return self.register_info
+
+ @abstractmethod
+ def get_register_context(self):
+ """Get the scripted thread register context
+
+ Returns:
+ str: A byte representing all register's value.
+ """
+ pass
class PassthroughScriptedProcess(ScriptedProcess):
driving_target = None
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 08de0605b324..e4bbcd5ddcd9 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -104,6 +104,8 @@ public:
bool IsArtificial() const;
+ bool IsSynthetic() const;
+
/// Return whether a frame recognizer decided this frame should not
/// be displayes in backtraces etc.
bool IsHidden() const;
diff --git a/lldb/include/lldb/API/SBFunction.h b/lldb/include/lldb/API/SBFunction.h
index 0a8aeeff1ea5..e703ae5dd63c 100644
--- a/lldb/include/lldb/API/SBFunction.h
+++ b/lldb/include/lldb/API/SBFunction.h
@@ -36,6 +36,8 @@ public:
const char *GetMangledName() const;
+ const char *GetBaseName() const;
+
lldb::SBInstructionList GetInstructions(lldb::SBTarget target);
lldb::SBInstructionList GetInstructions(lldb::SBTarget target,
diff --git a/lldb/include/lldb/API/SBSymbol.h b/lldb/include/lldb/API/SBSymbol.h
index a93bc7a7ae07..580458ede212 100644
--- a/lldb/include/lldb/API/SBSymbol.h
+++ b/lldb/include/lldb/API/SBSymbol.h
@@ -36,6 +36,8 @@ public:
const char *GetMangledName() const;
+ const char *GetBaseName() const;
+
lldb::SBInstructionList GetInstructions(lldb::SBTarget target);
lldb::SBInstructionList GetInstructions(lldb::SBTarget target,
diff --git a/lldb/include/lldb/API/SBSymbolContext.h b/lldb/include/lldb/API/SBSymbolContext.h
index 128b0b65b786..19f29c629d09 100644
--- a/lldb/include/lldb/API/SBSymbolContext.h
+++ b/lldb/include/lldb/API/SBSymbolContext.h
@@ -66,6 +66,7 @@ protected:
friend class SBTarget;
friend class SBSymbolContextList;
+ friend class lldb_private::ScriptInterpreter;
friend class lldb_private::python::SWIGBridge;
SBSymbolContext(const lldb_private::SymbolContext &sc_ptr);
diff --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h
index b6fc1a20e1e6..ed64a895717a 100644
--- a/lldb/include/lldb/Core/Architecture.h
+++ b/lldb/include/lldb/Core/Architecture.h
@@ -12,6 +12,7 @@
#include "lldb/Core/PluginInterface.h"
#include "lldb/Target/DynamicRegisterInfo.h"
#include "lldb/Target/MemoryTagManager.h"
+#include "lldb/Target/RegisterContextUnwind.h"
namespace lldb_private {
@@ -129,6 +130,14 @@ public:
RegisterContext &reg_context) const {
return false;
}
+
+ /// Return an UnwindPlan that allows architecture-defined rules for finding
+ /// saved registers, given a particular set of register values.
+ virtual lldb::UnwindPlanSP GetArchitectureUnwindPlan(
+ lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx,
+ std::shared_ptr<const UnwindPlan> current_unwindplan) {
+ return lldb::UnwindPlanSP();
+ }
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h
index 50a5d8783584..db186dd33d77 100644
--- a/lldb/include/lldb/Core/Disassembler.h
+++ b/lldb/include/lldb/Core/Disassembler.h
@@ -169,7 +169,7 @@ public:
virtual bool IsAuthenticated() = 0;
- bool CanSetBreakpoint ();
+ bool CanSetBreakpoint();
virtual size_t Decode(const Disassembler &disassembler,
const DataExtractor &data,
@@ -282,7 +282,7 @@ std::function<bool(const Instruction::Operand &)> FetchImmOp(int64_t &imm);
std::function<bool(const Instruction::Operand &)>
MatchOpType(Instruction::Operand::Type type);
-}
+} // namespace OperandMatchers
class InstructionList {
public:
@@ -316,20 +316,19 @@ public:
/// @param[in] ignore_calls
/// It true, then fine the first branch instruction that isn't
/// a function call (a branch that calls and returns to the next
- /// instruction). If false, find the instruction index of any
+ /// instruction). If false, find the instruction index of any
/// branch in the list.
- ///
+ ///
/// @param[out] found_calls
- /// If non-null, this will be set to true if any calls were found in
+ /// If non-null, this will be set to true if any calls were found in
/// extending the range.
- ///
+ ///
/// @return
/// The instruction index of the first branch that is at or past
- /// \a start. Returns UINT32_MAX if no matching branches are
+ /// \a start. Returns UINT32_MAX if no matching branches are
/// found.
//------------------------------------------------------------------
- uint32_t GetIndexOfNextBranchInstruction(uint32_t start,
- bool ignore_calls,
+ uint32_t GetIndexOfNextBranchInstruction(uint32_t start, bool ignore_calls,
bool *found_calls) const;
uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,
@@ -399,6 +398,7 @@ public:
eOptionMarkPCAddress =
(1u << 3), // Mark the disassembly line the contains the PC
eOptionShowControlFlowKind = (1u << 4),
+ eOptionVariableAnnotations = (1u << 5),
};
enum HexImmediateStyle {
@@ -566,6 +566,26 @@ private:
const Disassembler &operator=(const Disassembler &) = delete;
};
+/// Tracks live variable annotations across instructions and produces
+/// per-instruction "events" like `name = RDI` or `name = <undef>`.
+class VariableAnnotator {
+ struct VarState {
+ /// Display name.
+ std::string name;
+ /// Last printed location (empty means <undef>).
+ std::string last_loc;
+ };
+
+ // Live state from the previous instruction, keyed by Variable::GetID().
+ llvm::DenseMap<lldb::user_id_t, VarState> Live_;
+
+public:
+ /// Compute annotation strings for a single instruction and update `Live_`.
+ /// Returns only the events that should be printed *at this instruction*.
+ std::vector<std::string> annotate(Instruction &inst, Target &target,
+ const lldb::ModuleSP &module_sp);
+};
+
} // namespace lldb_private
#endif // LLDB_CORE_DISASSEMBLER_H
diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h
index d602edffb4c8..40916dc48a70 100644
--- a/lldb/include/lldb/Core/FormatEntity.h
+++ b/lldb/include/lldb/Core/FormatEntity.h
@@ -80,6 +80,7 @@ struct Entry {
FrameRegisterFlags,
FrameRegisterByName,
FrameIsArtificial,
+ FrameKind,
ScriptFrame,
FunctionID,
FunctionDidChange,
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index eb9a58c56889..47f1c6a8d80b 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -287,6 +287,17 @@ public:
/// Retrieve \c DemangledNameInfo of the demangled name held by this object.
const std::optional<DemangledNameInfo> &GetDemangledInfo() const;
+ /// Compute the base name (without namespace/class qualifiers) from the
+ /// demangled name.
+ ///
+ /// For a demangled name like "ns::MyClass<int>::templateFunc", this returns
+ /// just "templateFunc".
+ ///
+ /// \return
+ /// A ConstString containing the basename, or nullptr if computation
+ /// fails.
+ ConstString GetBaseName() const;
+
private:
/// If \c force is \c false, this function will re-use the previously
/// demangled name (if any). If \c force is \c true (or the mangled name
diff --git a/lldb/include/lldb/Core/Opcode.h b/lldb/include/lldb/Core/Opcode.h
index f680e0b91ab8..7bbd73d039f9 100644
--- a/lldb/include/lldb/Core/Opcode.h
+++ b/lldb/include/lldb/Core/Opcode.h
@@ -211,7 +211,7 @@ public:
if (bytes != nullptr && length > 0) {
m_type = type;
m_data.inst.length = length;
- assert(length < sizeof(m_data.inst.bytes));
+ assert(length <= sizeof(m_data.inst.bytes));
memcpy(m_data.inst.bytes, bytes, length);
m_byte_order = order;
} else {
diff --git a/lldb/include/lldb/Core/ProtocolServer.h b/lldb/include/lldb/Core/ProtocolServer.h
index 937256c10aec..fcb91ea203e1 100644
--- a/lldb/include/lldb/Core/ProtocolServer.h
+++ b/lldb/include/lldb/Core/ProtocolServer.h
@@ -22,6 +22,8 @@ public:
static ProtocolServer *GetOrCreate(llvm::StringRef name);
+ static llvm::Error Terminate();
+
static std::vector<llvm::StringRef> GetSupportedProtocols();
struct Connection {
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 8fcc5d37b91c..1f8464cf5cf8 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -159,7 +159,8 @@ public:
return data.GetByteSize() > 0;
}
- void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const;
+ void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi,
+ llvm::DIDumpOptions options = {}) const;
bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const;
diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h
index 20067f469895..847226167d58 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ protected:
///
/// The format being:
///
-/// <prefix>:<module uid>:<symbol uid>:<name>
+/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
///
/// The label string needs to stay valid for the entire lifetime
/// of this object.
struct FunctionCallLabel {
+ /// Arbitrary string which language plugins can interpret for their
+ /// own needs.
+ llvm::StringRef discriminator;
+
/// Unique identifier of the lldb_private::Module
/// which contains the symbol identified by \c symbol_id.
lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
///
/// The representation roundtrips through \c fromString:
/// \code{.cpp}
- /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+ /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
/// FunctionCallLabel label = *fromString(label);
///
/// assert (label.toString() == encoded);
diff --git a/lldb/include/lldb/Expression/IRMemoryMap.h b/lldb/include/lldb/Expression/IRMemoryMap.h
index 58b95c56c1c3..56f79444764e 100644
--- a/lldb/include/lldb/Expression/IRMemoryMap.h
+++ b/lldb/include/lldb/Expression/IRMemoryMap.h
@@ -65,7 +65,7 @@ public:
size_t size, Status &error);
void WriteScalarToMemory(lldb::addr_t process_address, Scalar &scalar,
size_t size, Status &error);
- void WritePointerToMemory(lldb::addr_t process_address, lldb::addr_t address,
+ void WritePointerToMemory(lldb::addr_t process_address, lldb::addr_t pointer,
Status &error);
void ReadMemory(uint8_t *bytes, lldb::addr_t process_address, size_t size,
Status &error);
diff --git a/lldb/include/lldb/Host/File.h b/lldb/include/lldb/Host/File.h
index 9e2d0abe0b1a..7402a2231735 100644
--- a/lldb/include/lldb/Host/File.h
+++ b/lldb/include/lldb/Host/File.h
@@ -382,15 +382,11 @@ public:
Unowned = false,
};
- NativeFile() : m_descriptor(kInvalidDescriptor), m_stream(kInvalidStream) {}
+ NativeFile();
- NativeFile(FILE *fh, bool transfer_ownership)
- : m_descriptor(kInvalidDescriptor), m_own_descriptor(false), m_stream(fh),
- m_options(), m_own_stream(transfer_ownership) {}
+ NativeFile(FILE *fh, bool transfer_ownership);
- NativeFile(int fd, OpenOptions options, bool transfer_ownership)
- : m_descriptor(fd), m_own_descriptor(transfer_ownership),
- m_stream(kInvalidStream), m_options(options), m_own_stream(false) {}
+ NativeFile(int fd, OpenOptions options, bool transfer_ownership);
~NativeFile() override { Close(); }
@@ -444,17 +440,19 @@ protected:
return ValueGuard(m_stream_mutex, StreamIsValidUnlocked());
}
- int m_descriptor;
+ int m_descriptor = kInvalidDescriptor;
bool m_own_descriptor = false;
mutable std::mutex m_descriptor_mutex;
- FILE *m_stream;
+ FILE *m_stream = kInvalidStream;
mutable std::mutex m_stream_mutex;
OpenOptions m_options{};
bool m_own_stream = false;
std::mutex offset_access_mutex;
+ bool is_windows_console = false;
+
private:
NativeFile(const NativeFile &) = delete;
const NativeFile &operator=(const NativeFile &) = delete;
diff --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h
index b6a95fffb2db..a6aaacd9d6fe 100644
--- a/lldb/include/lldb/Host/HostInfoBase.h
+++ b/lldb/include/lldb/Host/HostInfoBase.h
@@ -102,11 +102,19 @@ public:
/// member of the FileSpec is filled in.
static FileSpec GetSystemPluginDir();
+ /// Returns the directory containing the users home (e.g. `~/`). Only the
+ /// directory member of the FileSpec is filled in.
+ static FileSpec GetUserHomeDir();
+
+ /// Returns the directory containing the users lldb home (e.g. `~/.lldb/`).
+ /// Only the directory member of the FileSpec is filled in.
+ static FileSpec GetUserLLDBDir();
+
/// Returns the directory containing the user plugins. Only the directory
/// member of the FileSpec is filled in.
static FileSpec GetUserPluginDir();
- /// Returns the proces temporary directory. This directory will be cleaned up
+ /// Returns the process temporary directory. This directory will be cleaned up
/// when this process exits. Only the directory member of the FileSpec is
/// filled in.
static FileSpec GetProcessTempDir();
@@ -167,11 +175,13 @@ protected:
static bool ComputeTempFileBaseDirectory(FileSpec &file_spec);
static bool ComputeHeaderDirectory(FileSpec &file_spec);
static bool ComputeSystemPluginsDirectory(FileSpec &file_spec);
+ static bool ComputeUserHomeDirectory(FileSpec &file_spec);
+ static bool ComputeUserLLDBHomeDirectory(FileSpec &file_spec);
static bool ComputeUserPluginsDirectory(FileSpec &file_spec);
static void ComputeHostArchitectureSupport(ArchSpec &arch_32,
ArchSpec &arch_64);
};
-}
+} // namespace lldb_private
#endif
diff --git a/lldb/include/lldb/Host/JSONTransport.h b/lldb/include/lldb/Host/JSONTransport.h
index 0be60a8f3f96..210f33edace6 100644
--- a/lldb/include/lldb/Host/JSONTransport.h
+++ b/lldb/include/lldb/Host/JSONTransport.h
@@ -100,7 +100,8 @@ public:
virtual llvm::Expected<MainLoop::ReadHandleUP>
RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) = 0;
-protected:
+ // FIXME: Refactor mcp::Server to not directly access log on the transport.
+ // protected:
template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
}
@@ -139,9 +140,7 @@ public:
/// detail.
static constexpr size_t kReadBufferSize = 1024;
-protected:
- virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
- virtual std::string Encode(const llvm::json::Value &message) = 0;
+ // FIXME: Write should be protected.
llvm::Error Write(const llvm::json::Value &message) {
this->Logv("<-- {0}", message);
std::string output = Encode(message);
@@ -149,6 +148,10 @@ protected:
return m_out->Write(output.data(), bytes_written).takeError();
}
+protected:
+ virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
+ virtual std::string Encode(const llvm::json::Value &message) = 0;
+
llvm::SmallString<kReadBufferSize> m_buffer;
private:
diff --git a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
index d04841885660..734a394c1867 100644
--- a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
+++ b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
@@ -56,6 +56,7 @@ protected:
static std::string FindComponentInPath(llvm::StringRef path,
llvm::StringRef component);
};
-}
+
+} // namespace lldb_private
#endif
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
new file mode 100644
index 000000000000..8ef4b37d6ba1
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
+
+#include "ScriptedInterface.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/lldb-private.h"
+#include <optional>
+#include <string>
+
+namespace lldb_private {
+class ScriptedFrameInterface : virtual public ScriptedInterface {
+public:
+ virtual llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_obj = nullptr) = 0;
+
+ virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; }
+
+ virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; }
+
+ virtual std::optional<SymbolContext> GetSymbolContext() {
+ return std::nullopt;
+ }
+
+ virtual std::optional<std::string> GetFunctionName() { return std::nullopt; }
+
+ virtual std::optional<std::string> GetDisplayFunctionName() {
+ return std::nullopt;
+ }
+
+ virtual bool IsInlined() { return false; }
+
+ virtual bool IsArtificial() { return false; }
+
+ virtual bool IsHidden() { return false; }
+
+ virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; }
+
+ virtual std::optional<std::string> GetRegisterContext() {
+ return std::nullopt;
+ }
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
index a7cfc690b67d..bc58f344d36f 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h
@@ -44,6 +44,16 @@ public:
}
virtual StructuredData::ArraySP GetExtendedInfo() { return {}; }
+
+ virtual std::optional<std::string> GetScriptedFramePluginName() {
+ return std::nullopt;
+ }
+
+protected:
+ friend class ScriptedFrame;
+ virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+ return {};
+ }
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Interpreter/Options.h b/lldb/include/lldb/Interpreter/Options.h
index 864bda6f24c8..2d06605f5f0b 100644
--- a/lldb/include/lldb/Interpreter/Options.h
+++ b/lldb/include/lldb/Interpreter/Options.h
@@ -85,10 +85,10 @@ public:
void OutputFormattedUsageText(Stream &strm,
const OptionDefinition &option_def,
- uint32_t output_max_columns);
+ uint32_t output_max_columns, bool use_color);
void GenerateOptionUsage(Stream &strm, CommandObject &cmd,
- uint32_t screen_width);
+ uint32_t screen_width, bool use_color);
bool SupportsLongOption(const char *long_option);
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index dffb9b82abf3..024bbc90a9a3 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -26,6 +26,7 @@
#include "lldb/Host/PseudoTerminal.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h"
@@ -531,6 +532,10 @@ public:
return {};
}
+ virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() {
+ return {};
+ }
+
virtual lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() {
return {};
diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h
index 49f949022175..6e1ffcbe1f3e 100644
--- a/lldb/include/lldb/Protocol/MCP/Protocol.h
+++ b/lldb/include/lldb/Protocol/MCP/Protocol.h
@@ -18,6 +18,7 @@
#include <optional>
#include <string>
#include <variant>
+#include <vector>
namespace lldb_protocol::mcp {
@@ -38,11 +39,24 @@ struct Request {
/// The method's params.
std::optional<llvm::json::Value> params;
};
-
llvm::json::Value toJSON(const Request &);
bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
bool operator==(const Request &, const Request &);
+enum ErrorCode : signed {
+ /// Invalid JSON was received by the server. An error occurred on the server
+ /// while parsing the JSON text.
+ eErrorCodeParseError = -32700,
+ /// The JSON sent is not a valid Request object.
+ eErrorCodeInvalidRequest = -32600,
+ /// The method does not exist / is not available.
+ eErrorCodeMethodNotFound = -32601,
+ /// Invalid method parameter(s).
+ eErrorCodeInvalidParams = -32602,
+ /// Internal JSON-RPC error.
+ eErrorCodeInternalError = -32603,
+};
+
struct Error {
/// The error type that occurred.
int64_t code = 0;
@@ -52,9 +66,8 @@ struct Error {
/// Additional information about the error. The value of this member is
/// defined by the sender (e.g. detailed error information, nested errors
/// etc.).
- std::optional<llvm::json::Value> data;
+ std::optional<llvm::json::Value> data = std::nullopt;
};
-
llvm::json::Value toJSON(const Error &);
bool fromJSON(const llvm::json::Value &, Error &, llvm::json::Path);
bool operator==(const Error &, const Error &);
@@ -67,7 +80,6 @@ struct Response {
/// response.
std::variant<Error, llvm::json::Value> result;
};
-
llvm::json::Value toJSON(const Response &);
bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
bool operator==(const Response &, const Response &);
@@ -79,7 +91,6 @@ struct Notification {
/// The notification's params.
std::optional<llvm::json::Value> params;
};
-
llvm::json::Value toJSON(const Notification &);
bool fromJSON(const llvm::json::Value &, Notification &, llvm::json::Path);
bool operator==(const Notification &, const Notification &);
@@ -90,45 +101,9 @@ using Message = std::variant<Request, Response, Notification>;
// not force it to be checked early here.
static_assert(std::is_convertible_v<Message, Message>,
"Message is not convertible to itself");
-
bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
llvm::json::Value toJSON(const Message &);
-struct ToolCapability {
- /// Whether this server supports notifications for changes to the tool list.
- bool listChanged = false;
-};
-
-llvm::json::Value toJSON(const ToolCapability &);
-bool fromJSON(const llvm::json::Value &, ToolCapability &, llvm::json::Path);
-
-struct ResourceCapability {
- /// Whether this server supports notifications for changes to the resources
- /// list.
- bool listChanged = false;
-
- /// Whether subscriptions are supported.
- bool subscribe = false;
-};
-
-llvm::json::Value toJSON(const ResourceCapability &);
-bool fromJSON(const llvm::json::Value &, ResourceCapability &,
- llvm::json::Path);
-
-/// Capabilities that a server may support. Known capabilities are defined here,
-/// in this schema, but this is not a closed set: any server can define its own,
-/// additional capabilities.
-struct Capabilities {
- /// Tool capabilities of the server.
- ToolCapability tools;
-
- /// Resource capabilities of the server.
- ResourceCapability resources;
-};
-
-llvm::json::Value toJSON(const Capabilities &);
-bool fromJSON(const llvm::json::Value &, Capabilities &, llvm::json::Path);
-
/// A known resource that the server is capable of reading.
struct Resource {
/// The URI of this resource.
@@ -138,17 +113,25 @@ struct Resource {
std::string name;
/// A description of what this resource represents.
- std::string description;
+ std::string description = "";
/// The MIME type of this resource, if known.
- std::string mimeType;
+ std::string mimeType = "";
};
llvm::json::Value toJSON(const Resource &);
bool fromJSON(const llvm::json::Value &, Resource &, llvm::json::Path);
+/// The server’s response to a resources/list request from the client.
+struct ListResourcesResult {
+ std::vector<Resource> resources;
+};
+llvm::json::Value toJSON(const ListResourcesResult &);
+bool fromJSON(const llvm::json::Value &, ListResourcesResult &,
+ llvm::json::Path);
+
/// The contents of a specific resource or sub-resource.
-struct ResourceContents {
+struct TextResourceContents {
/// The URI of this resource.
std::string uri;
@@ -160,34 +143,37 @@ struct ResourceContents {
std::string mimeType;
};
-llvm::json::Value toJSON(const ResourceContents &);
-bool fromJSON(const llvm::json::Value &, ResourceContents &, llvm::json::Path);
+llvm::json::Value toJSON(const TextResourceContents &);
+bool fromJSON(const llvm::json::Value &, TextResourceContents &,
+ llvm::json::Path);
-/// The server's response to a resources/read request from the client.
-struct ResourceResult {
- std::vector<ResourceContents> contents;
+/// Sent from the client to the server, to read a specific resource URI.
+struct ReadResourceParams {
+ /// The URI of the resource to read. The URI can use any protocol; it is up to
+ /// the server how to interpret it.
+ std::string uri;
};
+llvm::json::Value toJSON(const ReadResourceParams &);
+bool fromJSON(const llvm::json::Value &, ReadResourceParams &,
+ llvm::json::Path);
-llvm::json::Value toJSON(const ResourceResult &);
-bool fromJSON(const llvm::json::Value &, ResourceResult &, llvm::json::Path);
+/// The server's response to a resources/read request from the client.
+struct ReadResourceResult {
+ std::vector<TextResourceContents> contents;
+};
+llvm::json::Value toJSON(const ReadResourceResult &);
+bool fromJSON(const llvm::json::Value &, ReadResourceResult &,
+ llvm::json::Path);
/// Text provided to or from an LLM.
struct TextContent {
/// The text content of the message.
std::string text;
};
-
llvm::json::Value toJSON(const TextContent &);
bool fromJSON(const llvm::json::Value &, TextContent &, llvm::json::Path);
-struct TextResult {
- std::vector<TextContent> content;
- bool isError = false;
-};
-
-llvm::json::Value toJSON(const TextResult &);
-bool fromJSON(const llvm::json::Value &, TextResult &, llvm::json::Path);
-
+/// Definition for a tool the client can call.
struct ToolDefinition {
/// Unique identifier for the tool.
std::string name;
@@ -198,12 +184,144 @@ struct ToolDefinition {
// JSON Schema for the tool's parameters.
std::optional<llvm::json::Value> inputSchema;
};
-
llvm::json::Value toJSON(const ToolDefinition &);
bool fromJSON(const llvm::json::Value &, ToolDefinition &, llvm::json::Path);
using ToolArguments = std::variant<std::monostate, llvm::json::Value>;
+/// Describes the name and version of an MCP implementation, with an optional
+/// title for UI representation.
+struct Implementation {
+ /// Intended for programmatic or logical use, but used as a display name in
+ /// past specs or fallback (if title isn’t present).
+ std::string name;
+
+ std::string version;
+
+ /// Intended for UI and end-user contexts — optimized to be human-readable and
+ /// easily understood, even by those unfamiliar with domain-specific
+ /// terminology.
+ ///
+ /// If not provided, the name should be used for display (except for Tool,
+ /// where annotations.title should be given precedence over using name, if
+ /// present).
+ std::string title = "";
+};
+llvm::json::Value toJSON(const Implementation &);
+bool fromJSON(const llvm::json::Value &, Implementation &, llvm::json::Path);
+
+/// Capabilities a client may support. Known capabilities are defined here, in
+/// this schema, but this is not a closed set: any client can define its own,
+/// additional capabilities.
+struct ClientCapabilities {};
+llvm::json::Value toJSON(const ClientCapabilities &);
+bool fromJSON(const llvm::json::Value &, ClientCapabilities &,
+ llvm::json::Path);
+
+/// Capabilities that a server may support. Known capabilities are defined here,
+/// in this schema, but this is not a closed set: any server can define its own,
+/// additional capabilities.
+struct ServerCapabilities {
+ bool supportsToolsList = false;
+ bool supportsResourcesList = false;
+ bool supportsResourcesSubscribe = false;
+
+ /// Utilities.
+ bool supportsCompletions = false;
+ bool supportsLogging = false;
+};
+llvm::json::Value toJSON(const ServerCapabilities &);
+bool fromJSON(const llvm::json::Value &, ServerCapabilities &,
+ llvm::json::Path);
+
+/// Initialization
+
+/// This request is sent from the client to the server when it first connects,
+/// asking it to begin initialization.
+struct InitializeParams {
+ /// The latest version of the Model Context Protocol that the client supports.
+ /// The client MAY decide to support older versions as well.
+ std::string protocolVersion;
+
+ ClientCapabilities capabilities;
+
+ Implementation clientInfo;
+};
+llvm::json::Value toJSON(const InitializeParams &);
+bool fromJSON(const llvm::json::Value &, InitializeParams &, llvm::json::Path);
+
+/// After receiving an initialize request from the client, the server sends this
+/// response.
+struct InitializeResult {
+ /// The version of the Model Context Protocol that the server wants to use.
+ /// This may not match the version that the client requested. If the client
+ /// cannot support this version, it MUST disconnect.
+ std::string protocolVersion;
+
+ ServerCapabilities capabilities;
+ Implementation serverInfo;
+
+ /// Instructions describing how to use the server and its features.
+ ///
+ /// This can be used by clients to improve the LLM's understanding of
+ /// available tools, resources, etc. It can be thought of like a "hint" to the
+ /// model. For example, this information MAY be added to the system prompt.
+ std::string instructions = "";
+};
+llvm::json::Value toJSON(const InitializeResult &);
+bool fromJSON(const llvm::json::Value &, InitializeResult &, llvm::json::Path);
+
+/// Special case parameter or result that has no value.
+using Void = std::monostate;
+llvm::json::Value toJSON(const Void &);
+bool fromJSON(const llvm::json::Value &, Void &, llvm::json::Path);
+
+/// The server's response to a `tools/list` request from the client.
+struct ListToolsResult {
+ std::vector<ToolDefinition> tools;
+};
+llvm::json::Value toJSON(const ListToolsResult &);
+bool fromJSON(const llvm::json::Value &, ListToolsResult &, llvm::json::Path);
+
+/// Supported content types, currently only TextContent, but the spec includes
+/// additional content types.
+using ContentBlock = TextContent;
+
+/// Used by the client to invoke a tool provided by the server.
+struct CallToolParams {
+ std::string name;
+ std::optional<llvm::json::Value> arguments;
+};
+llvm::json::Value toJSON(const CallToolParams &);
+bool fromJSON(const llvm::json::Value &, CallToolParams &, llvm::json::Path);
+
+/// The server’s response to a tool call.
+struct CallToolResult {
+ /// A list of content objects that represent the unstructured result of the
+ /// tool call.
+ std::vector<ContentBlock> content;
+
+ /// Whether the tool call ended in an error.
+ ///
+ /// If not set, this is assumed to be false (the call was successful).
+ ///
+ /// Any errors that originate from the tool SHOULD be reported inside the
+ /// result object, with `isError` set to true, not as an MCP protocol-level
+ /// error response. Otherwise, the LLM would not be able to see that an error
+ /// occurred and self-correct.
+ ///
+ /// However, any errors in finding the tool, an error indicating that the
+ /// server does not support tool calls, or any other exceptional conditions,
+ /// should be reported as an MCP error response.
+ bool isError = false;
+
+ /// An optional JSON object that represents the structured result of the tool
+ /// call.
+ std::optional<llvm::json::Value> structuredContent = std::nullopt;
+};
+llvm::json::Value toJSON(const CallToolResult &);
+bool fromJSON(const llvm::json::Value &, CallToolResult &, llvm::json::Path);
+
} // namespace lldb_protocol::mcp
#endif
diff --git a/lldb/include/lldb/Protocol/MCP/Resource.h b/lldb/include/lldb/Protocol/MCP/Resource.h
index 4835d340cd4c..158cffc71ea1 100644
--- a/lldb/include/lldb/Protocol/MCP/Resource.h
+++ b/lldb/include/lldb/Protocol/MCP/Resource.h
@@ -20,7 +20,7 @@ public:
virtual ~ResourceProvider() = default;
virtual std::vector<lldb_protocol::mcp::Resource> GetResources() const = 0;
- virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
+ virtual llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
ReadResource(llvm::StringRef uri) const = 0;
};
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 2ac05880de86..b674d5815955 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -9,40 +9,55 @@
#ifndef LLDB_PROTOCOL_MCP_SERVER_H
#define LLDB_PROTOCOL_MCP_SERVER_H
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/Host/MainLoop.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Resource.h"
#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Protocol/MCP/Transport.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
-#include <mutex>
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Signals.h"
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
namespace lldb_protocol::mcp {
-class Server {
+class Server : public MCPTransport::MessageHandler {
public:
- Server(std::string name, std::string version);
- virtual ~Server() = default;
+ Server(std::string name, std::string version,
+ std::unique_ptr<MCPTransport> transport_up,
+ lldb_private::MainLoop &loop);
+ ~Server() = default;
+
+ using NotificationHandler = std::function<void(const Notification &)>;
void AddTool(std::unique_ptr<Tool> tool);
void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider);
+ void AddNotificationHandler(llvm::StringRef method,
+ NotificationHandler handler);
+
+ llvm::Error Run();
protected:
- virtual Capabilities GetCapabilities() = 0;
+ ServerCapabilities GetCapabilities();
using RequestHandler =
std::function<llvm::Expected<Response>(const Request &)>;
- using NotificationHandler = std::function<void(const Notification &)>;
void AddRequestHandlers();
void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
- void AddNotificationHandler(llvm::StringRef method,
- NotificationHandler handler);
llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data);
- llvm::Expected<Response> Handle(Request request);
- void Handle(Notification notification);
+ llvm::Expected<Response> Handle(const Request &request);
+ void Handle(const Notification &notification);
llvm::Expected<Response> InitializeHandler(const Request &);
@@ -52,12 +67,21 @@ protected:
llvm::Expected<Response> ResourcesListHandler(const Request &);
llvm::Expected<Response> ResourcesReadHandler(const Request &);
- std::mutex m_mutex;
+ void Received(const Request &) override;
+ void Received(const Response &) override;
+ void Received(const Notification &) override;
+ void OnError(llvm::Error) override;
+ void OnClosed() override;
+
+ void TerminateLoop();
private:
const std::string m_name;
const std::string m_version;
+ std::unique_ptr<MCPTransport> m_transport_up;
+ lldb_private::MainLoop &m_loop;
+
llvm::StringMap<std::unique_ptr<Tool>> m_tools;
std::vector<std::unique_ptr<ResourceProvider>> m_resource_providers;
@@ -65,6 +89,42 @@ private:
llvm::StringMap<NotificationHandler> m_notification_handlers;
};
+class ServerInfoHandle;
+
+/// Information about this instance of lldb's MCP server for lldb-mcp to use to
+/// coordinate connecting an lldb-mcp client.
+struct ServerInfo {
+ std::string connection_uri;
+
+ /// Writes the server info into a unique file in `~/.lldb`.
+ static llvm::Expected<ServerInfoHandle> Write(const ServerInfo &);
+ /// Loads any server info saved in `~/.lldb`.
+ static llvm::Expected<std::vector<ServerInfo>> Load();
+};
+llvm::json::Value toJSON(const ServerInfo &);
+bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
+
+/// A handle that tracks the server info on disk and cleans up the disk record
+/// once it is no longer referenced.
+class ServerInfoHandle {
+public:
+ ServerInfoHandle();
+ explicit ServerInfoHandle(llvm::StringRef filename);
+ ~ServerInfoHandle();
+
+ ServerInfoHandle(ServerInfoHandle &&other);
+ ServerInfoHandle &operator=(ServerInfoHandle &&other) noexcept;
+
+ /// ServerIinfoHandle is not copyable.
+ /// @{
+ ServerInfoHandle(const ServerInfoHandle &) = delete;
+ ServerInfoHandle &operator=(const ServerInfoHandle &) = delete;
+ /// @}
+
+private:
+ llvm::SmallString<128> m_filename;
+};
+
} // namespace lldb_protocol::mcp
#endif
diff --git a/lldb/include/lldb/Protocol/MCP/Tool.h b/lldb/include/lldb/Protocol/MCP/Tool.h
index 96669d135716..6c9f05161f8e 100644
--- a/lldb/include/lldb/Protocol/MCP/Tool.h
+++ b/lldb/include/lldb/Protocol/MCP/Tool.h
@@ -10,6 +10,7 @@
#define LLDB_PROTOCOL_MCP_TOOL_H
#include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
#include <string>
@@ -20,7 +21,7 @@ public:
Tool(std::string name, std::string description);
virtual ~Tool() = default;
- virtual llvm::Expected<lldb_protocol::mcp::TextResult>
+ virtual llvm::Expected<lldb_protocol::mcp::CallToolResult>
Call(const lldb_protocol::mcp::ToolArguments &args) = 0;
virtual std::optional<llvm::json::Value> GetSchema() const {
diff --git a/lldb/include/lldb/Protocol/MCP/Transport.h b/lldb/include/lldb/Protocol/MCP/Transport.h
new file mode 100644
index 000000000000..47c2ccfc44df
--- /dev/null
+++ b/lldb/include/lldb/Protocol/MCP/Transport.h
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PROTOCOL_MCP_TRANSPORT_H
+#define LLDB_PROTOCOL_MCP_TRANSPORT_H
+
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/FunctionExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_protocol::mcp {
+
+/// Generic transport that uses the MCP protocol.
+using MCPTransport = lldb_private::Transport<Request, Response, Notification>;
+
+/// Generic logging callback, to allow the MCP server / client / transport layer
+/// to be independent of the lldb log implementation.
+using LogCallback = llvm::unique_function<void(llvm::StringRef message)>;
+
+class Transport final
+ : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
+public:
+ Transport(lldb::IOObjectSP in, lldb::IOObjectSP out,
+ LogCallback log_callback = {});
+ virtual ~Transport() = default;
+
+ /// Transport is not copyable.
+ /// @{
+ Transport(const Transport &) = delete;
+ void operator=(const Transport &) = delete;
+ /// @}
+
+ void Log(llvm::StringRef message) override;
+
+private:
+ LogCallback m_log_callback;
+};
+
+} // namespace lldb_protocol::mcp
+
+#endif
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index bbc615d9fdc3..ff67e002e5b0 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -332,12 +332,12 @@ public:
/// Resolves the function corresponding to the specified LLDB function
/// call \c label.
///
- /// \param[in] label The FunctionCallLabel to be resolved.
+ /// \param[in,out] label The FunctionCallLabel to be resolved.
///
/// \returns An llvm::Error if the specified \c label couldn't be resolved.
/// Returns the resolved function (as a SymbolContext) otherwise.
virtual llvm::Expected<SymbolContext>
- ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+ ResolveFunctionCallLabel(FunctionCallLabel &label) {
return llvm::createStringError("Not implemented");
}
@@ -488,13 +488,16 @@ public:
return false;
};
- /// Get number of loaded/parsed DWO files. This is emitted in "statistics
- /// dump"
+ /// Retrieves statistics about DWO files associated with this symbol file.
+ /// This function returns a DWOStats struct containing:
+ /// - The number of successfully loaded/parsed DWO files.
+ /// - The total number of DWO files encountered.
+ /// - The number of DWO CUs that failed to load due to errors.
+ /// If this symbol file does not support DWO files, all counts will be zero.
///
/// \returns
- /// A pair containing (loaded_dwo_count, total_dwo_count). If this
- /// symbol file doesn't support DWO files, both counts will be 0.
- virtual std::pair<uint32_t, uint32_t> GetDwoFileCounts() { return {0, 0}; }
+ /// A DWOStats struct with loaded, total, and error counts for DWO files.
+ virtual DWOStats GetDwoStats() { return {}; }
virtual lldb::TypeSP
MakeType(lldb::user_id_t uid, ConstString name,
diff --git a/lldb/include/lldb/Target/CoreFileMemoryRanges.h b/lldb/include/lldb/Target/CoreFileMemoryRanges.h
index 78d01acca324..ef56a02ddee2 100644
--- a/lldb/include/lldb/Target/CoreFileMemoryRanges.h
+++ b/lldb/include/lldb/Target/CoreFileMemoryRanges.h
@@ -50,7 +50,7 @@ class CoreFileMemoryRanges
CoreFileMemoryRange> {
public:
/// Finalize and merge all overlapping ranges in this collection. Ranges
- /// will be seperated based on permissions.
+ /// will be separated based on permissions.
Status FinalizeCoreFileSaveRanges();
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
index 534516085091..dafa41c11327 100644
--- a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
+++ b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
@@ -24,6 +24,9 @@ public:
return lldb::eStopReasonInstrumentation;
}
+ std::optional<uint32_t>
+ GetSuggestedStackFrameIndex(bool inlined_stack) override;
+
const char *GetDescription() override;
bool DoShouldNotify(Event *event_ptr) override { return true; }
diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h
index b10a364823b8..52c28fd76da9 100644
--- a/lldb/include/lldb/Target/RegisterContextUnwind.h
+++ b/lldb/include/lldb/Target/RegisterContextUnwind.h
@@ -21,6 +21,7 @@
namespace lldb_private {
class UnwindLLDB;
+class ArchitectureArm;
class RegisterContextUnwind : public lldb_private::RegisterContext {
public:
@@ -72,6 +73,25 @@ public:
// above asynchronous trap handlers (sigtramp) for instance.
bool BehavesLikeZerothFrame() const override;
+protected:
+ // Provide a location for where THIS function saved the CALLER's register
+ // value, or a frame "below" this one saved it. That is, this function doesn't
+ // modify the register, it may call a function that does & saved it to stack.
+ //
+ // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable --
+ // this will happen for a volatile register being queried mid-stack. Instead
+ // of floating frame 0's contents of that register up the stack (which may or
+ // may not be the value of that reg when the function was executing), we won't
+ // return any value.
+ //
+ // If a non-volatile register (a "preserved" register, a callee-preserved
+ // register) is requested mid-stack, and no frames "below" the requested stack
+ // have saved the register anywhere, it is safe to assume that frame 0's
+ // register value is the same.
+ lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister(
+ uint32_t lldb_regnum,
+ lldb_private::UnwindLLDB::ConcreteRegisterLocation &regloc);
+
private:
enum FrameType {
eNormalFrame,
@@ -86,6 +106,8 @@ private:
// UnwindLLDB needs to pass around references to ConcreteRegisterLocations
friend class UnwindLLDB;
+ // Architecture may need to retrieve caller register values from this frame
+ friend class ArchitectureArm;
// Returns true if we have an unwind loop -- the same stack frame unwinding
// multiple times.
@@ -130,27 +152,6 @@ private:
void PropagateTrapHandlerFlagFromUnwindPlan(
std::shared_ptr<const UnwindPlan> unwind_plan);
- // Provide a location for where THIS function saved the CALLER's register
- // value
- // Or a frame "below" this one saved it, i.e. a function called by this one,
- // preserved a register that this
- // function didn't modify/use.
- //
- // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable --
- // this will happen for a volatile register being queried mid-stack. Instead
- // of floating frame 0's contents of that register up the stack (which may or
- // may not be the value of that reg when the function was executing), we won't
- // return any value.
- //
- // If a non-volatile register (a "preserved" register) is requested mid-stack
- // and no frames "below" the requested
- // stack have saved the register anywhere, it is safe to assume that frame 0's
- // register values are still the same
- // as the requesting frame's.
- lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister(
- uint32_t lldb_regnum,
- lldb_private::UnwindLLDB::ConcreteRegisterLocation &regloc);
-
std::optional<UnwindPlan::Row::AbstractRegisterLocation>
GetAbstractRegisterLocation(uint32_t lldb_regnum, lldb::RegisterKind &kind);
@@ -202,6 +203,8 @@ private:
std::shared_ptr<const UnwindPlan> GetFullUnwindPlanForFrame();
+ lldb::UnwindPlanSP TryAdoptArchitectureUnwindPlan();
+
void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
void UnwindLogMsgVerbose(const char *fmt, ...)
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index d4104bfe49d2..cdbe8ae3c677 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -60,10 +60,9 @@ public:
/// local variables.
History,
- /// An artificial stack frame (e.g. a synthesized result of inferring
- /// missing tail call frames from a backtrace) with limited support for
- /// local variables.
- Artificial
+ /// An synthetic stack frame (e.g. a synthesized result from script
+ /// resource) possibly without support for local variables or register.
+ Synthetic
};
/// Construct a StackFrame object without supplying a RegisterContextSP.
@@ -109,7 +108,8 @@ public:
StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx,
lldb::user_id_t concrete_frame_idx, lldb::addr_t cfa,
bool cfa_is_valid, lldb::addr_t pc, Kind frame_kind,
- bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr);
+ bool artificial, bool behaves_like_zeroth_frame,
+ const SymbolContext *sc_ptr);
StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx,
lldb::user_id_t concrete_frame_idx,
@@ -398,7 +398,10 @@ public:
///
/// \return
/// true if this is an inlined frame.
- bool IsInlined();
+ virtual bool IsInlined();
+
+ /// Query whether this frame is synthetic.
+ bool IsSynthetic() const;
/// Query whether this frame is part of a historical backtrace.
bool IsHistorical() const;
@@ -406,12 +409,12 @@ public:
/// Query whether this frame is artificial (e.g a synthesized result of
/// inferring missing tail call frames from a backtrace). Artificial frames
/// may have limited support for inspecting variables.
- bool IsArtificial() const;
+ virtual bool IsArtificial() const;
/// Query whether this frame should be hidden from backtraces. Frame
/// recognizers can customize this behavior and hide distracting
/// system implementation details this way.
- bool IsHidden();
+ virtual bool IsHidden();
/// Language plugins can use this API to report language-specific
/// runtime information about this compile unit, such as additional
@@ -422,13 +425,13 @@ public:
///
/// /// \return
/// A C-String containing the function demangled name. Can be null.
- const char *GetFunctionName();
+ virtual const char *GetFunctionName();
/// Get the frame's demangled display name.
///
/// /// \return
/// A C-String containing the function demangled display name. Can be null.
- const char *GetDisplayFunctionName();
+ virtual const char *GetDisplayFunctionName();
/// Query this frame to find what frame it is in this Thread's
/// StackFrameList.
@@ -540,18 +543,7 @@ protected:
bool HasCachedData() const;
-private:
- /// Private methods, called from GetValueForVariableExpressionPath.
- /// See that method for documentation of parameters and return value.
- lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
- llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
- lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
- llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
- uint32_t options, lldb::VariableSP &var_sp, Status &error);
-
- /// For StackFrame only.
+ /// For StackFrame and derived classes only.
/// \{
lldb::ThreadWP m_thread_wp;
uint32_t m_frame_index;
@@ -571,6 +563,10 @@ private:
/// Does this frame have a CFA? Different from CFA == LLDB_INVALID_ADDRESS.
bool m_cfa_is_valid;
Kind m_stack_frame_kind;
+ /// Is this an artificial stack frame (e.g. a synthesized result of inferring
+ /// missing tail call frames from a backtrace) with limited support for
+ /// local variables. Orthogonal to `StackFrame::Kind`.
+ bool m_artificial;
/// Whether this frame behaves like the zeroth frame, in the sense
/// that its pc value might not immediately follow a call (and thus might
@@ -584,6 +580,17 @@ private:
StreamString m_disassembly;
std::recursive_mutex m_mutex;
+private:
+ /// Private methods, called from GetValueForVariableExpressionPath.
+ /// See that method for documentation of parameters and return value.
+ lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath(
+ llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+ uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
+ lldb::ValueObjectSP DILGetValueForVariableExpressionPath(
+ llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
+ uint32_t options, lldb::VariableSP &var_sp, Status &error);
+
StackFrame(const StackFrame &) = delete;
const StackFrame &operator=(const StackFrame &) = delete;
};
diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h
index e5a6e942d743..ea9aab86b8ea 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -46,6 +46,9 @@ public:
/// Mark a stack frame as the currently selected frame and return its index.
uint32_t SetSelectedFrame(lldb_private::StackFrame *frame);
+ /// Resets the selected frame index of this object.
+ void ClearSelectedFrameIndex();
+
/// Get the currently selected frame index.
/// We should only call SelectMostRelevantFrame if (a) the user hasn't already
/// selected a frame, and (b) if this really is a user facing
@@ -172,6 +175,15 @@ protected:
/// The currently selected frame. An optional is used to record whether anyone
/// has set the selected frame on this stack yet. We only let recognizers
/// change the frame if this is the first time GetSelectedFrame is called.
+ ///
+ /// Thread-safety:
+ /// This member is not protected by a mutex.
+ /// LLDB really only should have an opinion about the selected frame index
+ /// when a process stops, before control gets handed back to the user.
+ /// After that, it's up to them to change it whenever they feel like it.
+ /// If two parts of lldb decided they wanted to be in control of the selected
+ /// frame index on stop the right way to fix it would need to be some explicit
+ /// negotiation for who gets to control this.
std::optional<uint32_t> m_selected_frame_idx;
/// Protect access to m_selected_frame_idx. Always acquire after m_list_mutex
diff --git a/lldb/include/lldb/Target/StackID.h b/lldb/include/lldb/Target/StackID.h
index fddbc8e48dfd..c2a5d733dcd6 100644
--- a/lldb/include/lldb/Target/StackID.h
+++ b/lldb/include/lldb/Target/StackID.h
@@ -10,7 +10,6 @@
#define LLDB_TARGET_STACKID_H
#include "lldb/Core/AddressRange.h"
-#include "lldb/lldb-private.h"
namespace lldb_private {
@@ -18,15 +17,11 @@ class Process;
class StackID {
public:
- // Constructors and Destructors
StackID() = default;
explicit StackID(lldb::addr_t pc, lldb::addr_t cfa,
SymbolContextScope *symbol_scope, Process *process);
- StackID(const StackID &rhs)
- : m_pc(rhs.m_pc), m_cfa(rhs.m_cfa), m_symbol_scope(rhs.m_symbol_scope) {}
-
~StackID() = default;
lldb::addr_t GetPC() const { return m_pc; }
@@ -51,41 +46,28 @@ public:
void Dump(Stream *s);
- // Operators
- const StackID &operator=(const StackID &rhs) {
- if (this != &rhs) {
- m_pc = rhs.m_pc;
- m_cfa = rhs.m_cfa;
- m_symbol_scope = rhs.m_symbol_scope;
- }
- return *this;
- }
-
protected:
friend class StackFrame;
void SetPC(lldb::addr_t pc, Process *process);
void SetCFA(lldb::addr_t cfa, Process *process);
- lldb::addr_t m_pc =
- LLDB_INVALID_ADDRESS; // The pc value for the function/symbol for this
- // frame. This will
- // only get used if the symbol scope is nullptr (the code where we are
- // stopped is not represented by any function or symbol in any shared
- // library).
- lldb::addr_t m_cfa =
- LLDB_INVALID_ADDRESS; // The call frame address (stack pointer) value
- // at the beginning of the function that uniquely
- // identifies this frame (along with m_symbol_scope
- // below)
- SymbolContextScope *m_symbol_scope =
- nullptr; // If nullptr, there is no block or symbol for this frame.
- // If not nullptr, this will either be the scope for the
- // lexical block for the frame, or the scope for the
- // symbol. Symbol context scopes are always be unique
- // pointers since the are part of the Block and Symbol
- // objects and can easily be used to tell if a stack ID
- // is the same as another.
+ /// The pc value for the function/symbol for this frame. This will only get
+ /// used if the symbol scope is nullptr (the code where we are stopped is not
+ /// represented by any function or symbol in any shared library).
+ lldb::addr_t m_pc = LLDB_INVALID_ADDRESS;
+
+ /// The call frame address (stack pointer) value at the beginning of the
+ /// function that uniquely identifies this frame (along with m_symbol_scope
+ /// below)
+ lldb::addr_t m_cfa = LLDB_INVALID_ADDRESS;
+
+ /// If nullptr, there is no block or symbol for this frame. If not nullptr,
+ /// this will either be the scope for the lexical block for the frame, or the
+ /// scope for the symbol. Symbol context scopes are always be unique pointers
+ /// since the are part of the Block and Symbol objects and can easily be used
+ /// to tell if a stack ID is the same as another.
+ SymbolContextScope *m_symbol_scope = nullptr;
};
bool operator==(const StackID &lhs, const StackID &rhs);
diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index 55dff8861a9a..d6983bb0b9d2 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -123,6 +123,25 @@ struct StatsSuccessFail {
uint32_t failures = 0;
};
+/// Holds statistics about DWO (Debug With Object) files.
+struct DWOStats {
+ uint32_t loaded_dwo_file_count = 0;
+ uint32_t dwo_file_count = 0;
+ uint32_t dwo_error_count = 0;
+
+ DWOStats &operator+=(const DWOStats &rhs) {
+ loaded_dwo_file_count += rhs.loaded_dwo_file_count;
+ dwo_file_count += rhs.dwo_file_count;
+ dwo_error_count += rhs.dwo_error_count;
+ return *this;
+ }
+
+ friend DWOStats operator+(DWOStats lhs, const DWOStats &rhs) {
+ lhs += rhs;
+ return lhs;
+ }
+};
+
/// A class that represents statistics for a since lldb_private::Module.
struct ModuleStats {
llvm::json::Value ToJSON() const;
@@ -153,8 +172,7 @@ struct ModuleStats {
bool symtab_stripped = false;
bool debug_info_had_variable_errors = false;
bool debug_info_had_incomplete_types = false;
- uint32_t dwo_file_count = 0;
- uint32_t loaded_dwo_file_count = 0;
+ DWOStats dwo_stats;
};
struct ConstStringStats {
diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index 368ec51d8189..cdd6a6fbe6aa 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -97,6 +97,12 @@ public:
/// and silently continue again one more time.
virtual bool WasContinueInterrupted(Thread &thread) { return false; }
+ virtual uint32_t GetStopReasonDataCount() const { return 0; }
+ virtual uint64_t GetStopReasonDataAtIndex(uint32_t idx) {
+ // Handle all the common cases that have no data.
+ return 0;
+ }
+
// Sometimes the thread plan logic will know that it wants a given stop to
// stop or not, regardless of what the ordinary logic for that StopInfo would
// dictate. The main example of this is the ThreadPlanCallFunction, which
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 6ede7fa301a8..688c056da263 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -479,6 +479,11 @@ public:
bool SetSelectedFrameByIndexNoisily(uint32_t frame_idx,
Stream &output_stream);
+ /// Resets the selected frame index of this object.
+ void ClearSelectedFrameIndex() {
+ return GetStackFrameList()->ClearSelectedFrameIndex();
+ }
+
void SetDefaultFileAndLineToSelectedFrame() {
GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame();
}
diff --git a/lldb/include/lldb/Target/UnwindLLDB.h b/lldb/include/lldb/Target/UnwindLLDB.h
index 88180b37fd93..29b3ab9c9029 100644
--- a/lldb/include/lldb/Target/UnwindLLDB.h
+++ b/lldb/include/lldb/Target/UnwindLLDB.h
@@ -22,6 +22,7 @@
namespace lldb_private {
class RegisterContextUnwind;
+class ArchitectureArm;
class UnwindLLDB : public lldb_private::Unwind {
public:
@@ -37,6 +38,7 @@ public:
protected:
friend class lldb_private::RegisterContextUnwind;
+ friend class lldb_private::ArchitectureArm;
/// An UnwindPlan::Row::AbstractRegisterLocation, combined with the register
/// context and memory for a specific stop point, is used to create a
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 5c99341ad888..7db184ad6722 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -260,6 +260,11 @@ inline std::string TrimAndPad(llvm::StringRef str, size_t visible_length,
return result;
}
+inline size_t ColumnWidth(llvm::StringRef str) {
+ std::string stripped = ansi::StripAnsiTerminalCodes(str);
+ return llvm::sys::locale::columnWidth(stripped);
+}
+
} // namespace ansi
} // namespace lldb_private
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 709f0639135f..1d10755c46e3 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -21,7 +21,9 @@ enum class NodeKind {
eArraySubscriptNode,
eBitExtractionNode,
eErrorNode,
+ eFloatLiteralNode,
eIdentifierNode,
+ eIntegerLiteralNode,
eMemberOfNode,
eUnaryOpNode,
};
@@ -178,6 +180,52 @@ private:
int64_t m_last_index;
};
+enum class IntegerTypeSuffix { None, Long, LongLong };
+
+class IntegerLiteralNode : public ASTNode {
+public:
+ IntegerLiteralNode(uint32_t location, llvm::APInt value, uint32_t radix,
+ bool is_unsigned, IntegerTypeSuffix type)
+ : ASTNode(location, NodeKind::eIntegerLiteralNode),
+ m_value(std::move(value)), m_radix(radix), m_is_unsigned(is_unsigned),
+ m_type(type) {}
+
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+ const llvm::APInt &GetValue() const { return m_value; }
+ uint32_t GetRadix() const { return m_radix; }
+ bool IsUnsigned() const { return m_is_unsigned; }
+ IntegerTypeSuffix GetTypeSuffix() const { return m_type; }
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eIntegerLiteralNode;
+ }
+
+private:
+ llvm::APInt m_value;
+ uint32_t m_radix;
+ bool m_is_unsigned;
+ IntegerTypeSuffix m_type;
+};
+
+class FloatLiteralNode : public ASTNode {
+public:
+ FloatLiteralNode(uint32_t location, llvm::APFloat value)
+ : ASTNode(location, NodeKind::eFloatLiteralNode),
+ m_value(std::move(value)) {}
+
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
+
+ const llvm::APFloat &GetValue() const { return m_value; }
+
+ static bool classof(const ASTNode *node) {
+ return node->GetKind() == NodeKind::eFloatLiteralNode;
+ }
+
+private:
+ llvm::APFloat m_value;
+};
+
/// This class contains one Visit method for each specialized type of
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
/// the correct function in the DIL expression evaluator for evaluating that
@@ -195,6 +243,10 @@ public:
Visit(const ArraySubscriptNode *node) = 0;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const BitFieldExtractionNode *node) = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IntegerLiteralNode *node) = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP>
+ Visit(const FloatLiteralNode *node) = 0;
};
} // namespace lldb_private::dil
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 45e29b3ddcd7..5a48c2c989f4 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -54,6 +54,15 @@ private:
Visit(const ArraySubscriptNode *node) override;
llvm::Expected<lldb::ValueObjectSP>
Visit(const BitFieldExtractionNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IntegerLiteralNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP>
+ Visit(const FloatLiteralNode *node) override;
+
+ llvm::Expected<CompilerType>
+ PickIntegerType(lldb::TypeSystemSP type_system,
+ std::shared_ptr<ExecutionContextScope> ctx,
+ const IntegerLiteralNode *literal);
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index 9c1ba9768025..4345e6ce7f26 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -28,12 +28,14 @@ public:
arrow,
coloncolon,
eof,
+ float_constant,
identifier,
+ integer_constant,
l_paren,
l_square,
minus,
- numeric_constant,
period,
+ plus,
r_paren,
r_square,
star,
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 9eda7bac4a36..90df109337dc 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -96,6 +96,9 @@ private:
std::string ParseIdExpression();
std::string ParseUnqualifiedId();
std::optional<int64_t> ParseIntegerConstant();
+ ASTNodeUP ParseNumericLiteral();
+ ASTNodeUP ParseIntegerLiteral();
+ ASTNodeUP ParseFloatingPointLiteral();
void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 483dce98ea42..af5656b3dcad 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -187,6 +187,7 @@ class SaveCoreOptions;
class Scalar;
class ScriptInterpreter;
class ScriptInterpreterLocker;
+class ScriptedFrameInterface;
class ScriptedMetadata;
class ScriptedBreakpointInterface;
class ScriptedPlatformInterface;
@@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame>
typedef std::shared_ptr<lldb_private::ScriptSummaryFormat>
ScriptSummaryFormatSP;
typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP;
+typedef std::shared_ptr<lldb_private::ScriptedFrameInterface>
+ ScriptedFrameInterfaceSP;
typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP;
typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface>
ScriptedPlatformInterfaceUP;
diff --git a/lldb/packages/Python/lldbsuite/test/configuration.py b/lldb/packages/Python/lldbsuite/test/configuration.py
index 5e3810992d17..c2c46b94454a 100644
--- a/lldb/packages/Python/lldbsuite/test/configuration.py
+++ b/lldb/packages/Python/lldbsuite/test/configuration.py
@@ -64,6 +64,9 @@ filecheck = None
# Path to the yaml2obj tool. Not optional.
yaml2obj = None
+# Path to the yaml2macho-core tool. Not optional.
+yaml2macho_core = None
+
# The arch might dictate some specific CFLAGS to be passed to the toolchain to build
# the inferior programs. The global variable cflags_extras provides a hook to do
# just that.
@@ -174,3 +177,11 @@ def get_yaml2obj_path():
"""
if yaml2obj and os.path.lexists(yaml2obj):
return yaml2obj
+
+
+def get_yaml2macho_core_path():
+ """
+ Get the path to the yaml2macho-core tool.
+ """
+ if yaml2macho_core and os.path.lexists(yaml2macho_core):
+ return yaml2macho_core
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index bd10bcc3d6ce..16a58cfc10b9 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -1,4 +1,8 @@
# System modules
+
+# allow the use of the `list[str]` type hint in Python 3.8
+from __future__ import annotations
+
from functools import wraps
from packaging import version
import ctypes
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 47a3c2ed2fc9..2966ac04227c 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -280,6 +280,9 @@ def parseOptionsAndInitTestdirs():
configuration.llvm_tools_dir = args.llvm_tools_dir
configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
+ configuration.yaml2macho_core = shutil.which(
+ "yaml2macho-core", path=args.llvm_tools_dir
+ )
if not configuration.get_filecheck_path():
logging.warning("No valid FileCheck executable; some tests may fail...")
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 0fc85fcc4d2d..b7077f8d8cc5 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1702,6 +1702,29 @@ class Base(unittest.TestCase):
command += ["--max-size=%d" % max_size]
self.runBuildCommand(command)
+ def yaml2macho_core(self, yaml_path, obj_path, uuids=None):
+ """
+ Create a Mach-O corefile at the given path from a yaml file.
+
+ Throws subprocess.CalledProcessError if the object could not be created.
+ """
+ yaml2macho_core_bin = configuration.get_yaml2macho_core_path()
+ if not yaml2macho_core_bin:
+ self.assertTrue(False, "No valid yaml2macho-core executable specified")
+ if uuids != None:
+ command = [
+ yaml2macho_core_bin,
+ "-i",
+ yaml_path,
+ "-o",
+ obj_path,
+ "-u",
+ uuids,
+ ]
+ else:
+ command = [yaml2macho_core_bin, "-i", yaml_path, "-o", obj_path]
+ self.runBuildCommand(command)
+
def cleanup(self, dictionary=None):
"""Platform specific way to do cleanup after build."""
module = builder_module()
@@ -2264,7 +2287,9 @@ class TestBase(Base, metaclass=LLDBTestCaseFactory):
given list of completions"""
interp = self.dbg.GetCommandInterpreter()
match_strings = lldb.SBStringList()
- interp.HandleCompletion(command, len(command), 0, max_completions, match_strings)
+ interp.HandleCompletion(
+ command, len(command), 0, max_completions, match_strings
+ )
# match_strings is a 1-indexed list, so we have to slice...
self.assertCountEqual(
completions, list(match_strings)[1:], "List of returned completion is wrong"
diff --git a/lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h b/lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h
index 491acb52be87..a1aa7e1a69f3 100644
--- a/lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h
+++ b/lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h
@@ -8,7 +8,9 @@
// 0 -> Post-c88580c layout
// 1 -> Post-27c83382d83dc layout
// 2 -> Post-769c42f4a552a layout
-// 3 -> padding-less no_unique_address-based layout (introduced in 27c83382d83dc)
+// 3 -> Post-f5e687d7bf49c layout
+// 4 -> padding-less no_unique_address-based layout (introduced in
+// 27c83382d83dc)
namespace std {
namespace __lldb {
@@ -42,7 +44,7 @@ template <class _ToPad> class __compressed_pair_padding {
? 0
: sizeof(_ToPad) - __datasizeof_v<_ToPad>];
};
-#elif COMPRESSED_PAIR_REV > 1 && COMPRESSED_PAIR_REV < 3
+#elif COMPRESSED_PAIR_REV > 1 && COMPRESSED_PAIR_REV < 4
template <class _ToPad>
inline const bool __is_reference_or_unpadded_object =
(std::is_empty<_ToPad>::value && !__lldb_is_final<_ToPad>::value) ||
@@ -125,6 +127,27 @@ public:
_LLDB_NO_UNIQUE_ADDRESS T3 Initializer3; \
_LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T3> __padding3_;
#elif COMPRESSED_PAIR_REV == 3
+#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
+ struct { \
+ [[__gnu__::__aligned__( \
+ alignof(T2))]] _LLDB_NO_UNIQUE_ADDRESS T1 Initializer1; \
+ _LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T1> __padding1_; \
+ _LLDB_NO_UNIQUE_ADDRESS T2 Initializer2; \
+ _LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T2> __padding2_; \
+ }
+
+#define _LLDB_COMPRESSED_TRIPLE(T1, Initializer1, T2, Initializer2, T3, \
+ Initializer3) \
+ struct { \
+ [[using __gnu__: __aligned__(alignof(T2)), \
+ __aligned__(alignof(T3))]] _LLDB_NO_UNIQUE_ADDRESS T1 Initializer1; \
+ _LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T1> __padding1_; \
+ _LLDB_NO_UNIQUE_ADDRESS T2 Initializer2; \
+ _LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T2> __padding2_; \
+ _LLDB_NO_UNIQUE_ADDRESS T3 Initializer3; \
+ _LLDB_NO_UNIQUE_ADDRESS __compressed_pair_padding<T3> __padding3_; \
+ }
+#elif COMPRESSED_PAIR_REV == 4
#define _LLDB_COMPRESSED_PAIR(T1, Name1, T2, Name2) \
_LLDB_NO_UNIQUE_ADDRESS T1 Name1; \
_LLDB_NO_UNIQUE_ADDRESS T2 Name2
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index 0608ac3fd83b..51debcf477a9 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -1490,12 +1490,17 @@ class DebugAdapterServer(DebugCommunication):
init_commands: list[str] = [],
log_file: Optional[TextIO] = None,
env: Optional[dict[str, str]] = None,
+ additional_args: list[str] = [],
):
self.process = None
self.connection = None
if executable is not None:
process, connection = DebugAdapterServer.launch(
- executable=executable, connection=connection, env=env, log_file=log_file
+ executable=executable,
+ connection=connection,
+ env=env,
+ log_file=log_file,
+ additional_args=additional_args,
)
self.process = process
self.connection = connection
@@ -1528,6 +1533,8 @@ class DebugAdapterServer(DebugCommunication):
env: Optional[dict[str, str]] = None,
log_file: Optional[TextIO] = None,
connection: Optional[str] = None,
+ connection_timeout: Optional[int] = None,
+ additional_args: list[str] = [],
) -> tuple[subprocess.Popen, Optional[str]]:
adapter_env = os.environ.copy()
if env is not None:
@@ -1537,10 +1544,17 @@ class DebugAdapterServer(DebugCommunication):
adapter_env["LLDBDAP_LOG"] = log_file
args = [executable]
+ # Add additional arguments first (like --no-lldbinit)
+ args.extend(additional_args)
+
if connection is not None:
args.append("--connection")
args.append(connection)
+ if connection_timeout is not None:
+ args.append("--connection-timeout")
+ args.append(str(connection_timeout))
+
process = subprocess.Popen(
args,
stdin=subprocess.PIPE,
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index c23b2e73fb45..fffd4c23d6fc 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -21,6 +21,7 @@ class DAPTestCaseBase(TestBase):
self,
lldbDAPEnv: Optional[dict[str, str]] = None,
connection: Optional[str] = None,
+ additional_args: Optional[list[str]] = None,
):
"""Create the Visual Studio Code debug adapter"""
self.assertTrue(
@@ -33,15 +34,17 @@ class DAPTestCaseBase(TestBase):
init_commands=self.setUpCommands(),
log_file=log_file_path,
env=lldbDAPEnv,
+ additional_args=additional_args or [],
)
def build_and_create_debug_adapter(
self,
lldbDAPEnv: Optional[dict[str, str]] = None,
dictionary: Optional[dict] = None,
+ additional_args: Optional[list[str]] = None,
):
self.build(dictionary=dictionary)
- self.create_debug_adapter(lldbDAPEnv)
+ self.create_debug_adapter(lldbDAPEnv, additional_args=additional_args)
def build_and_create_debug_adapter_for_attach(self):
"""Variant of build_and_create_debug_adapter that builds a uniquely
@@ -450,6 +453,25 @@ class DAPTestCaseBase(TestBase):
return disassembled_instructions, disassembled_instructions[memoryReference]
+ def _build_error_message(self, base_message, response):
+ """Build a detailed error message from a DAP response.
+ Extracts error information from various possible locations in the response structure.
+ """
+ error_msg = base_message
+ if response:
+ if "message" in response:
+ error_msg += " (%s)" % response["message"]
+ elif "body" in response and "error" in response["body"]:
+ if "format" in response["body"]["error"]:
+ error_msg += " (%s)" % response["body"]["error"]["format"]
+ else:
+ error_msg += " (error in body)"
+ else:
+ error_msg += " (no error details available)"
+ else:
+ error_msg += " (no response)"
+ return error_msg
+
def attach(
self,
*,
@@ -477,9 +499,8 @@ class DAPTestCaseBase(TestBase):
if expectFailure:
return response
if not (response and response["success"]):
- self.assertTrue(
- response["success"], "attach failed (%s)" % (response["message"])
- )
+ error_msg = self._build_error_message("attach failed", response)
+ self.assertTrue(response and response["success"], error_msg)
def launch(
self,
@@ -508,10 +529,8 @@ class DAPTestCaseBase(TestBase):
if expectFailure:
return response
if not (response and response["success"]):
- self.assertTrue(
- response["success"],
- "launch failed (%s)" % (response["body"]["error"]["format"]),
- )
+ error_msg = self._build_error_message("launch failed", response)
+ self.assertTrue(response and response["success"], error_msg)
def build_and_launch(
self,
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp
index 4ea79d336e08..34323bc5a2c3 100644
--- a/lldb/source/API/SBCommandInterpreter.cpp
+++ b/lldb/source/API/SBCommandInterpreter.cpp
@@ -208,7 +208,7 @@ void SBCommandInterpreter::HandleCommandsFromFile(
LLDB_INSTRUMENT_VA(this, file, override_context, options, result);
if (!IsValid()) {
- result->AppendError("SBCommandInterpreter is not valid.");
+ result->AppendError("SBCommandInterpreter is not valid");
return;
}
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index b12cfceacd75..b6724bb0c411 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -1118,6 +1118,22 @@ bool SBFrame::IsArtificial() const {
return false;
}
+bool SBFrame::IsSynthetic() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return false;
+ }
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->IsSynthetic();
+
+ return false;
+}
+
bool SBFrame::IsHidden() const {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp
index 19861f6af364..65b02d6b309c 100644
--- a/lldb/source/API/SBFunction.cpp
+++ b/lldb/source/API/SBFunction.cpp
@@ -79,6 +79,15 @@ const char *SBFunction::GetMangledName() const {
return nullptr;
}
+const char *SBFunction::GetBaseName() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (!m_opaque_ptr)
+ return nullptr;
+
+ return m_opaque_ptr->GetMangled().GetBaseName().AsCString();
+}
+
bool SBFunction::operator==(const SBFunction &rhs) const {
LLDB_INSTRUMENT_VA(this, rhs);
diff --git a/lldb/source/API/SBHostOS.cpp b/lldb/source/API/SBHostOS.cpp
index a77a703bba37..cd9b8571df3e 100644
--- a/lldb/source/API/SBHostOS.cpp
+++ b/lldb/source/API/SBHostOS.cpp
@@ -86,15 +86,7 @@ SBFileSpec SBHostOS::GetLLDBPath(lldb::PathType path_type) {
SBFileSpec SBHostOS::GetUserHomeDirectory() {
LLDB_INSTRUMENT();
-
- FileSpec homedir;
- FileSystem::Instance().GetHomeDirectory(homedir);
- FileSystem::Instance().Resolve(homedir);
-
- SBFileSpec sb_fspec;
- sb_fspec.SetFileSpec(homedir);
-
- return sb_fspec;
+ return HostInfo::GetUserHomeDir();
}
lldb::thread_t SBHostOS::ThreadCreate(const char *name,
diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp
index 3b59119494f3..3030c8329212 100644
--- a/lldb/source/API/SBSymbol.cpp
+++ b/lldb/source/API/SBSymbol.cpp
@@ -79,6 +79,15 @@ const char *SBSymbol::GetMangledName() const {
return name;
}
+const char *SBSymbol::GetBaseName() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (!m_opaque_ptr)
+ return nullptr;
+
+ return m_opaque_ptr->GetMangled().GetBaseName().AsCString();
+}
+
bool SBSymbol::operator==(const SBSymbol &rhs) const {
LLDB_INSTRUMENT_VA(this, rhs);
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index ec68b2a4b6f3..4e4aa48bc9a2 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -157,52 +157,8 @@ size_t SBThread::GetStopReasonDataCount() {
if (exe_ctx) {
if (exe_ctx->HasThreadScope()) {
StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo();
- if (stop_info_sp) {
- StopReason reason = stop_info_sp->GetStopReason();
- switch (reason) {
- case eStopReasonInvalid:
- case eStopReasonNone:
- case eStopReasonTrace:
- case eStopReasonExec:
- case eStopReasonPlanComplete:
- case eStopReasonThreadExiting:
- case eStopReasonInstrumentation:
- case eStopReasonProcessorTrace:
- case eStopReasonVForkDone:
- case eStopReasonHistoryBoundary:
- // There is no data for these stop reasons.
- return 0;
-
- case eStopReasonBreakpoint: {
- break_id_t site_id = stop_info_sp->GetValue();
- lldb::BreakpointSiteSP bp_site_sp(
- exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID(
- site_id));
- if (bp_site_sp)
- return bp_site_sp->GetNumberOfConstituents() * 2;
- else
- return 0; // Breakpoint must have cleared itself...
- } break;
-
- case eStopReasonWatchpoint:
- return 1;
-
- case eStopReasonSignal:
- return 1;
-
- case eStopReasonInterrupt:
- return 1;
-
- case eStopReasonException:
- return 1;
-
- case eStopReasonFork:
- return 1;
-
- case eStopReasonVFork:
- return 1;
- }
- }
+ if (stop_info_sp)
+ return stop_info_sp->GetStopReasonDataCount();
}
} else {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
@@ -220,63 +176,8 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
if (exe_ctx->HasThreadScope()) {
Thread *thread = exe_ctx->GetThreadPtr();
StopInfoSP stop_info_sp = thread->GetStopInfo();
- if (stop_info_sp) {
- StopReason reason = stop_info_sp->GetStopReason();
- switch (reason) {
- case eStopReasonInvalid:
- case eStopReasonNone:
- case eStopReasonTrace:
- case eStopReasonExec:
- case eStopReasonPlanComplete:
- case eStopReasonThreadExiting:
- case eStopReasonInstrumentation:
- case eStopReasonProcessorTrace:
- case eStopReasonVForkDone:
- case eStopReasonHistoryBoundary:
- // There is no data for these stop reasons.
- return 0;
-
- case eStopReasonBreakpoint: {
- break_id_t site_id = stop_info_sp->GetValue();
- lldb::BreakpointSiteSP bp_site_sp(
- exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID(
- site_id));
- if (bp_site_sp) {
- uint32_t bp_index = idx / 2;
- BreakpointLocationSP bp_loc_sp(
- bp_site_sp->GetConstituentAtIndex(bp_index));
- if (bp_loc_sp) {
- if (idx & 1) {
- // Odd idx, return the breakpoint location ID
- return bp_loc_sp->GetID();
- } else {
- // Even idx, return the breakpoint ID
- return bp_loc_sp->GetBreakpoint().GetID();
- }
- }
- }
- return LLDB_INVALID_BREAK_ID;
- } break;
-
- case eStopReasonWatchpoint:
- return stop_info_sp->GetValue();
-
- case eStopReasonSignal:
- return stop_info_sp->GetValue();
-
- case eStopReasonInterrupt:
- return stop_info_sp->GetValue();
-
- case eStopReasonException:
- return stop_info_sp->GetValue();
-
- case eStopReasonFork:
- return stop_info_sp->GetValue();
-
- case eStopReasonVFork:
- return stop_info_sp->GetValue();
- }
- }
+ if (stop_info_sp)
+ return stop_info_sp->GetStopReasonDataAtIndex(idx);
}
} else {
LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 38ec375c0307..de0a7e709341 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -609,12 +609,12 @@ protected:
const size_t num_files = m_options.m_filenames.GetSize();
if (num_files == 0) {
if (!GetDefaultFile(target, file, result)) {
- result.AppendError("No file supplied and no default file available.");
+ result.AppendError("no file supplied and no default file available");
return;
}
} else if (num_files > 1) {
- result.AppendError("Only one file at a time is allowed for file and "
- "line breakpoints.");
+ result.AppendError("only one file at a time is allowed for file and "
+ "line breakpoints");
return;
} else
file = m_options.m_filenames.GetFileSpecAtIndex(0);
@@ -784,7 +784,7 @@ protected:
}
result.SetStatus(eReturnStatusSuccessFinishResult);
} else if (!bp_sp) {
- result.AppendError("Breakpoint creation failed: No breakpoint created.");
+ result.AppendError("breakpoint creation failed: no breakpoint created");
}
}
@@ -940,7 +940,7 @@ protected:
size_t num_breakpoints = breakpoints.GetSize();
if (num_breakpoints == 0) {
- result.AppendError("No breakpoints exist to be enabled.");
+ result.AppendError("no breakpoints exist to be enabled");
return;
}
@@ -1048,7 +1048,7 @@ protected:
size_t num_breakpoints = breakpoints.GetSize();
if (num_breakpoints == 0) {
- result.AppendError("No breakpoints exist to be disabled.");
+ result.AppendError("no breakpoints exist to be disabled");
return;
}
@@ -1224,7 +1224,7 @@ protected:
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
- result.AppendError("Invalid breakpoint ID.");
+ result.AppendError("invalid breakpoint ID");
}
}
}
@@ -1318,7 +1318,7 @@ protected:
// Early return if there's no breakpoint at all.
if (num_breakpoints == 0) {
- result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ result.AppendError("breakpoint clear: no breakpoint cleared");
return;
}
@@ -1364,7 +1364,7 @@ protected:
output_stream.EOL();
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
- result.AppendError("Breakpoint clear: No breakpoint cleared.");
+ result.AppendError("breakpoint clear: no breakpoint cleared");
}
}
@@ -1459,7 +1459,7 @@ protected:
size_t num_breakpoints = breakpoints.GetSize();
if (num_breakpoints == 0) {
- result.AppendError("No breakpoints exist to be deleted.");
+ result.AppendError("no breakpoints exist to be deleted");
return;
}
@@ -1504,7 +1504,7 @@ protected:
}
}
if (valid_bp_ids.GetSize() == 0) {
- result.AppendError("No disabled breakpoints.");
+ result.AppendError("no disabled breakpoints");
return;
}
} else {
@@ -1712,7 +1712,7 @@ protected:
const size_t argc = command.GetArgumentCount();
if (argc == 0) {
- result.AppendError("No names provided.");
+ result.AppendError("no names provided");
return;
}
@@ -1799,7 +1799,7 @@ public:
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (!m_name_options.m_name.OptionWasSet()) {
- result.AppendError("No name option provided.");
+ result.AppendError("no name option provided");
return;
}
@@ -1813,7 +1813,7 @@ protected:
size_t num_breakpoints = breakpoints.GetSize();
if (num_breakpoints == 0) {
- result.AppendError("No breakpoints, cannot add names.");
+ result.AppendError("no breakpoints, cannot add names");
return;
}
@@ -1825,7 +1825,7 @@ protected:
if (result.Succeeded()) {
if (valid_bp_ids.GetSize() == 0) {
- result.AppendError("No breakpoints specified, cannot add names.");
+ result.AppendError("no breakpoints specified, cannot add names");
return;
}
size_t num_valid_ids = valid_bp_ids.GetSize();
@@ -1873,7 +1873,7 @@ public:
protected:
void DoExecute(Args &command, CommandReturnObject &result) override {
if (!m_name_options.m_name.OptionWasSet()) {
- result.AppendError("No name option provided.");
+ result.AppendError("no name option provided");
return;
}
@@ -1887,7 +1887,7 @@ protected:
size_t num_breakpoints = breakpoints.GetSize();
if (num_breakpoints == 0) {
- result.AppendError("No breakpoints, cannot delete names.");
+ result.AppendError("no breakpoints, cannot delete names");
return;
}
@@ -1899,7 +1899,7 @@ protected:
if (result.Succeeded()) {
if (valid_bp_ids.GetSize() == 0) {
- result.AppendError("No breakpoints specified, cannot delete names.");
+ result.AppendError("no breakpoints specified, cannot delete names");
return;
}
ConstString bp_name(m_name_options.m_name.GetCurrentValue());
diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp
index 3049eb8c20db..a3293f0f7966 100644
--- a/lldb/source/Commands/CommandObjectCommands.cpp
+++ b/lldb/source/Commands/CommandObjectCommands.cpp
@@ -418,7 +418,7 @@ protected:
if ((pos != std::string::npos) && (pos > 0))
raw_command_string = raw_command_string.substr(pos);
} else {
- result.AppendError("Error parsing command string. No alias created.");
+ result.AppendError("error parsing command string. No alias created");
return;
}
@@ -2888,7 +2888,7 @@ protected:
size_t num_args = command.GetArgumentCount();
if (num_args == 0) {
- result.AppendError("No command was specified.");
+ result.AppendError("no command was specified");
return;
}
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 70e687e19ac6..c0553d2c6c8b 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.cpp
+++ b/lldb/source/Commands/CommandObjectDisassemble.cpp
@@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
}
} break;
+ case 'v':
+ enable_variable_annotations = true;
+ break;
+
case '\x01':
force = true;
break;
@@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
end_addr = LLDB_INVALID_ADDRESS;
symbol_containing_addr = LLDB_INVALID_ADDRESS;
raw = false;
+ enable_variable_annotations = false;
plugin_name.clear();
Target *target =
@@ -503,8 +508,9 @@ void CommandObjectDisassemble::DoExecute(Args &command,
"\"disassemble\" arguments are specified as options.\n");
const int terminal_width =
GetCommandInterpreter().GetDebugger().GetTerminalWidth();
+ const bool use_color = GetCommandInterpreter().GetDebugger().GetUseColor();
GetOptions()->GenerateOptionUsage(result.GetErrorStream(), *this,
- terminal_width);
+ terminal_width, use_color);
return;
}
@@ -528,6 +534,9 @@ void CommandObjectDisassemble::DoExecute(Args &command,
if (m_options.raw)
options |= Disassembler::eOptionRawOuput;
+ if (m_options.enable_variable_annotations)
+ options |= Disassembler::eOptionVariableAnnotations;
+
llvm::Expected<std::vector<AddressRange>> ranges =
GetRangesForSelectedMode(result);
if (!ranges) {
diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h
index 4fbcd72d1c04..eed44adce5ff 100644
--- a/lldb/source/Commands/CommandObjectDisassemble.h
+++ b/lldb/source/Commands/CommandObjectDisassemble.h
@@ -78,6 +78,7 @@ public:
// in SetOptionValue if anything the selects a location is set.
lldb::addr_t symbol_containing_addr = 0;
bool force = false;
+ bool enable_variable_annotations = false;
};
CommandObjectDisassemble(CommandInterpreter &interpreter);
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 56926999d167..88a02dce35b9 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -140,7 +140,7 @@ protected:
} else {
StopInfoSP stop_info_sp = thread->GetStopInfo();
if (!stop_info_sp) {
- result.AppendError("No arguments provided, and no stop info.");
+ result.AppendError("no arguments provided, and no stop info");
return;
}
@@ -148,7 +148,7 @@ protected:
}
if (!valobj_sp) {
- result.AppendError("No diagnosis available.");
+ result.AppendError("no diagnosis available");
return;
}
@@ -310,7 +310,7 @@ protected:
if (frame_idx == 0) {
// If you are already at the bottom of the stack, then just warn
// and don't reset the frame.
- result.AppendError("Already at the bottom of the stack.");
+ result.AppendError("already at the bottom of the stack");
return;
} else
frame_idx = 0;
@@ -335,7 +335,7 @@ protected:
if (frame_idx == num_frames - 1) {
// If we are already at the top of the stack, just warn and don't
// reset the frame.
- result.AppendError("Already at the top of the stack.");
+ result.AppendError("already at the top of the stack");
return;
} else
frame_idx = num_frames - 1;
@@ -349,7 +349,8 @@ protected:
command[0].c_str());
m_options.GenerateOptionUsage(
result.GetErrorStream(), *this,
- GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
+ GetCommandInterpreter().GetDebugger().GetUseColor());
return;
}
diff --git a/lldb/source/Commands/CommandObjectLog.cpp b/lldb/source/Commands/CommandObjectLog.cpp
index 17efae189b05..0c01da0b5683 100644
--- a/lldb/source/Commands/CommandObjectLog.cpp
+++ b/lldb/source/Commands/CommandObjectLog.cpp
@@ -547,7 +547,7 @@ protected:
Timer::SetQuiet(!increment);
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else
- result.AppendError("Could not convert increment value to boolean.");
+ result.AppendError("could not convert increment value to boolean");
}
if (!result.Succeeded()) {
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index af1ff3e84510..5786e757ef7e 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -365,6 +365,8 @@ protected:
return;
}
+ ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
+
CompilerType compiler_type;
Status error;
@@ -520,7 +522,7 @@ protected:
--pointer_count;
}
- auto size_or_err = compiler_type.GetByteSize(nullptr);
+ auto size_or_err = compiler_type.GetByteSize(exe_scope);
if (!size_or_err) {
result.AppendErrorWithFormat(
"unable to get the byte size of the type '%s'\n%s",
@@ -640,7 +642,7 @@ protected:
if (!m_format_options.GetFormatValue().OptionWasSet())
m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault);
- auto size_or_err = compiler_type.GetByteSize(nullptr);
+ auto size_or_err = compiler_type.GetByteSize(exe_scope);
if (!size_or_err) {
result.AppendError(llvm::toString(size_or_err.takeError()));
return;
@@ -800,7 +802,6 @@ protected:
output_stream_p = &result.GetOutputStream();
}
- ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
if (compiler_type.GetOpaqueQualType()) {
for (uint32_t i = 0; i < item_count; ++i) {
addr_t item_addr = addr + (i * item_byte_size);
diff --git a/lldb/source/Commands/CommandObjectMultiword.cpp b/lldb/source/Commands/CommandObjectMultiword.cpp
index c99b75ff2914..a369557cca84 100644
--- a/lldb/source/Commands/CommandObjectMultiword.cpp
+++ b/lldb/source/Commands/CommandObjectMultiword.cpp
@@ -159,7 +159,7 @@ void CommandObjectMultiword::Execute(const char *args_string,
auto sub_command = args[0].ref();
if (sub_command.empty()) {
- result.AppendError("Need to specify a non-empty subcommand.");
+ result.AppendError("need to specify a non-empty subcommand");
return;
}
diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 84c576e721e7..7d326404a550 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -259,7 +259,7 @@ protected:
if (!exe_module_sp)
exe_module_sp = target->GetExecutableModule();
if (!exe_module_sp) {
- result.AppendWarning("Could not get executable module after launch.");
+ result.AppendWarning("could not get executable module after launch");
} else {
const char *archname =
diff --git a/lldb/source/Commands/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp
index f11e27f01c8a..c5ab9e9f05be 100644
--- a/lldb/source/Commands/CommandObjectProtocolServer.cpp
+++ b/lldb/source/Commands/CommandObjectProtocolServer.cpp
@@ -15,6 +15,7 @@
#include "lldb/Utility/UriParser.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FormatAdapters.h"
+#include <string>
using namespace llvm;
using namespace lldb;
@@ -28,7 +29,7 @@ public:
CommandObjectProtocolServerStart(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "protocol-server start",
"start protocol server",
- "protocol-server start <protocol> <connection>") {
+ "protocol-server start <protocol> [<connection>]") {
AddSimpleArgumentList(lldb::eArgTypeProtocol, eArgRepeatPlain);
AddSimpleArgumentList(lldb::eArgTypeConnectURL, eArgRepeatPlain);
}
@@ -51,15 +52,13 @@ protected:
return;
}
- if (args.GetArgumentCount() < 2) {
- result.AppendError("no connection specified");
- return;
- }
- llvm::StringRef connection_uri = args.GetArgumentAtIndex(1);
+ std::string connection_uri = "listen://[localhost]:0";
+ if (args.GetArgumentCount() >= 2)
+ connection_uri = args.GetArgumentAtIndex(1);
const char *connection_error =
- "unsupported connection specifier, expected 'accept:///path' or "
- "'listen://[host]:port', got '{0}'.";
+ "unsupported connection specifier, expected 'accept:///path' "
+ "or 'listen://[host]:port', got '{0}'.";
auto uri = lldb_private::URI::Parse(connection_uri);
if (!uri) {
result.AppendErrorWithFormatv(connection_error, connection_uri);
diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp
index 7e7d3f065b62..0b4599b16ef0 100644
--- a/lldb/source/Commands/CommandObjectSource.cpp
+++ b/lldb/source/Commands/CommandObjectSource.cpp
@@ -513,7 +513,7 @@ protected:
"No selected frame to use to find the default source.");
return false;
} else if (!cur_frame->HasDebugInformation()) {
- result.AppendError("No debug info for the selected frame.");
+ result.AppendError("no debug info for the selected frame");
return false;
} else {
const SymbolContext &sc =
@@ -553,11 +553,11 @@ protected:
}
}
if (!m_module_list.GetSize()) {
- result.AppendError("No modules match the input.");
+ result.AppendError("no modules match the input");
return;
}
} else if (target.GetImages().GetSize() == 0) {
- result.AppendError("The target has no associated executable images.");
+ result.AppendError("the target has no associated executable images");
return;
}
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index dbebbbd38093..004542e3e6ae 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -2420,7 +2420,7 @@ protected:
result.GetErrorStream().SetAddressByteSize(addr_byte_size);
if (command.GetArgumentCount() == 0) {
- result.AppendError("file option must be specified.");
+ result.AppendError("file option must be specified");
return;
} else {
// Dump specified images (by basename or fullpath)
@@ -3565,13 +3565,13 @@ protected:
ThreadList threads(process->GetThreadList());
if (threads.GetSize() == 0) {
- result.AppendError("The process must be paused to use this command.");
+ result.AppendError("the process must be paused to use this command");
return;
}
ThreadSP thread(threads.GetThreadAtIndex(0));
if (!thread) {
- result.AppendError("The process must be paused to use this command.");
+ result.AppendError("the process must be paused to use this command");
return;
}
@@ -4075,7 +4075,8 @@ public:
default:
m_options.GenerateOptionUsage(
result.GetErrorStream(), *this,
- GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
+ GetCommandInterpreter().GetDebugger().GetUseColor());
syntax_error = true;
break;
}
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 57c23d533fb9..bbec714642ec 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -1570,7 +1570,7 @@ protected:
uint32_t frame_idx = frame_sp->GetFrameIndex();
if (frame_sp->IsInlined()) {
- result.AppendError("Don't know how to return from inlined frames.");
+ result.AppendError("don't know how to return from inlined frames");
return;
}
diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp
index e79c3b8939fa..12effed12a3c 100644
--- a/lldb/source/Commands/CommandObjectWatchpoint.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -44,7 +44,7 @@ static bool CheckTargetForWatchpointOperations(Target &target,
bool process_is_valid =
target.GetProcessSP() && target.GetProcessSP()->IsAlive();
if (!process_is_valid) {
- result.AppendError("There's no process or it is not alive.");
+ result.AppendError("there's no process or it is not alive");
return false;
}
// Target passes our checks, return true.
@@ -243,7 +243,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
target, command, wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -298,7 +298,7 @@ protected:
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0) {
- result.AppendError("No watchpoints exist to be enabled.");
+ result.AppendError("no watchpoints exist to be enabled");
return;
}
@@ -314,7 +314,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
target, command, wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -366,7 +366,7 @@ protected:
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0) {
- result.AppendError("No watchpoints exist to be disabled.");
+ result.AppendError("no watchpoints exist to be disabled");
return;
}
@@ -385,7 +385,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
target, command, wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -476,7 +476,7 @@ protected:
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0) {
- result.AppendError("No watchpoints exist to be deleted.");
+ result.AppendError("no watchpoints exist to be deleted");
return;
}
@@ -500,7 +500,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -596,7 +596,7 @@ protected:
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0) {
- result.AppendError("No watchpoints exist to be ignored.");
+ result.AppendError("no watchpoints exist to be ignored");
return;
}
@@ -611,7 +611,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
target, command, wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -715,7 +715,7 @@ protected:
size_t num_watchpoints = watchpoints.GetSize();
if (num_watchpoints == 0) {
- result.AppendError("No watchpoints exist to be modified.");
+ result.AppendError("no watchpoints exist to be modified");
return;
}
@@ -728,7 +728,7 @@ protected:
std::vector<uint32_t> wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(
target, command, wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
diff --git a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
index 32cb80b421fd..062bf75eb8ae 100644
--- a/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpointCommand.cpp
@@ -376,7 +376,7 @@ protected:
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
valid_wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -470,7 +470,7 @@ protected:
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
valid_wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
@@ -525,7 +525,7 @@ protected:
std::vector<uint32_t> valid_wp_ids;
if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
valid_wp_ids)) {
- result.AppendError("Invalid watchpoints specification.");
+ result.AppendError("invalid watchpoints specification");
return;
}
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 17d72cdcad12..595b3d08abec 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1,232 +1,354 @@
include "OptionsBase.td"
let Command = "target modules dump symtab" in {
- def tm_sort : Option<"sort", "s">, Group<1>,
- Desc<"Supply a sort order when dumping the symbol table.">,
- EnumArg<"SortOrder">;
- def tm_smn : Option<"show-mangled-names", "m">, Group<1>,
- Desc<"Do not demangle symbol names before showing them.">;
+ def tm_sort : Option<"sort", "s">,
+ Group<1>,
+ Desc<"${S}upply a sort order when dumping the symbol table.">,
+ EnumArg<"SortOrder">;
+ def tm_smn : Option<"show-mangled-names", "m">,
+ Group<1>,
+ Desc<"Do not de${m}angle symbol names before showing them.">;
}
let Command = "target modules dump separate debug info" in {
- def tm_json : Option<"json", "j">, Group<1>,
- Desc<"Output the details in JSON format.">;
- def tm_errors_only : Option<"errors-only", "e">, Group<1>,
- Desc<"Filter to show only debug info files with errors.">;
- def tm_force_load_all_debug_info : Option<"force-load-all-debug-info", "f">,
- Group<1>,
- Desc<"Load all debug info files.">;
+ def tm_json : Option<"json", "j">,
+ Group<1>,
+ Desc<"Output the details in ${J}SON format.">;
+ def tm_errors_only
+ : Option<"errors-only", "e">,
+ Group<1>,
+ Desc<"Filter to show only debug info files with ${e}rrors.">;
+ def tm_force_load_all_debug_info
+ : Option<"force-load-all-debug-info", "f">,
+ Group<1>,
+ Desc<"${F}orce load all debug info files.">;
}
let Command = "help" in {
def help_hide_aliases : Option<"hide-aliases", "a">,
- Desc<"Hide aliases in the command list.">;
+ Desc<"Hide ${a}liases in the command list.">;
def help_hide_user : Option<"hide-user-commands", "u">,
- Desc<"Hide user-defined commands from the list.">;
+ Desc<"Hide ${u}ser-defined commands from the list.">;
def help_show_hidden : Option<"show-hidden-commands", "h">,
- Desc<"Include commands prefixed with an underscore.">;
+ Desc<"Include commands prefixed with an underscore.">;
}
let Command = "settings set" in {
- def setset_global : Option<"global", "g">,
- Desc<"Apply the new value to the global default value.">;
- def setset_force : Option<"force", "f">,
- Desc<"Force an empty value to be accepted as the default.">;
- def setset_exists : Option<"exists", "e">,
- Desc<"Set the setting if it exists, but do not cause the command to raise "
- "an error if it does not exist.">;
+ def setset_global
+ : Option<"global", "g">,
+ Desc<"Apply the new value to the ${g}lobal default value.">;
+ def setset_force
+ : Option<"force", "f">,
+ Desc<"${F}orce an empty value to be accepted as the default.">;
+ def setset_exists
+ : Option<"exists", "e">,
+ Desc<"Set the setting if it ${e}xists, but do not cause the command to "
+ "raise an error if it does not exist.">;
}
let Command = "settings write" in {
- def setwrite_file : Option<"file", "f">, Required, Arg<"Filename">,
- Completion<"DiskFile">,
- Desc<"The file into which to write the settings.">;
+ def setwrite_file : Option<"file", "f">,
+ Required,
+ Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The ${f}ile into which to write the settings.">;
def setwrite_append : Option<"append", "a">,
- Desc<"Append to saved settings file if it exists.">;
+ Desc<"${A}ppend to saved settings file if it exists.">;
}
let Command = "settings read" in {
- def setread_file : Option<"file", "f">, Required, Arg<"Filename">,
- Completion<"DiskFile">,
- Desc<"The file from which to read the settings.">;
+ def setread_file : Option<"file", "f">,
+ Required,
+ Arg<"Filename">,
+ Completion<"DiskFile">,
+ Desc<"The ${f}ile from which to read the settings.">;
}
let Command = "settings clear" in {
- def setclear_all : Option<"all", "a">,
- Desc<"Clear all settings.">;
+ def setclear_all : Option<"all", "a">, Desc<"Clear ${a}ll settings.">;
}
let Command = "settings show" in {
def setshow_defaults : Option<"defaults", "d">,
- Desc<"Include default values if defined.">;
+ Desc<"Include ${d}efault values if defined.">;
}
let Command = "breakpoint list" in {
// FIXME: We need to add an "internal" command, and then add this sort of
// thing to it. But I need to see it for now, and don't want to wait.
def blist_internal : Option<"internal", "i">,
- Desc<"Show debugger internal breakpoints">;
- def blist_brief : Option<"brief", "b">, Group<1>,
- Desc<"Give a brief description of the breakpoint (no location info).">;
- def blist_full : Option<"full", "f">, Group<2>,
- Desc<"Give a full description of the breakpoint and its locations.">;
- def blist_verbose : Option<"verbose", "v">, Group<3>,
- Desc<"Explain everything we know about the breakpoint (for debugging "
- "debugger bugs).">;
- def blist_dummy_bp : Option<"dummy-breakpoints", "D">,
- Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is "
- "provided, which prime new targets.">;
+ Desc<"Show debugger ${i}nternal breakpoints">;
+ def blist_brief : Option<"brief", "b">,
+ Group<1>,
+ Desc<"Give a ${b}rief description of the breakpoint (no "
+ "location info).">;
+ def blist_full
+ : Option<"full", "f">,
+ Group<2>,
+ Desc<"Give a ${f}ull description of the breakpoint and its locations.">;
+ def blist_verbose : Option<"verbose", "v">,
+ Group<3>,
+ Desc<"Explain everything we know about the breakpoint "
+ "(for debugging debugger bugs).">;
+ def blist_dummy_bp
+ : Option<"dummy-breakpoints", "D">,
+ Desc<"List ${D}ummy breakpoints - i.e. breakpoints set before a file "
+ "is provided, which prime new targets.">;
}
let Command = "breakpoint modify" in {
- def breakpoint_modify_ignore_count : Option<"ignore-count", "i">, Group<1>,
- Arg<"Count">,
- Desc<"Set the number of times this breakpoint is skipped before stopping.">;
- def breakpoint_modify_one_shot : Option<"one-shot", "o">, Group<1>,
- Arg<"Boolean">,
- Desc<"The breakpoint is deleted the first time it stop causes a stop.">;
- def breakpoint_modify_thread_index : Option<"thread-index", "x">, Group<1>,
- Arg<"ThreadIndex">, Desc<"The breakpoint stops only for the thread whose "
- "index matches this argument.">;
- def breakpoint_modify_thread_id : Option<"thread-id", "t">, Group<1>,
- Arg<"ThreadID">, Desc<"The breakpoint stops only for the thread whose TID "
- "matches this argument. The token 'current' resolves to the current thread's ID.">;
- def breakpoint_modify_thread_name : Option<"thread-name", "T">, Group<1>,
- Arg<"ThreadName">, Desc<"The breakpoint stops only for the thread whose "
- "thread name matches this argument.">;
- def breakpoint_modify_queue_name : Option<"queue-name", "q">, Group<1>,
- Arg<"QueueName">, Desc<"The breakpoint stops only for threads in the queue "
- "whose name is given by this argument.">;
- def breakpoint_modify_condition : Option<"condition", "c">, Group<1>,
- Arg<"Expression">, Desc<"The breakpoint stops only if this condition "
- "expression evaluates to true.">;
+ def breakpoint_modify_ignore_count
+ : Option<"ignore-count", "i">,
+ Group<1>,
+ Arg<"Count">,
+ Desc<"Set the number of times this breakpoint is skipped before "
+ "stopping.">;
+ def breakpoint_modify_one_shot
+ : Option<"one-shot", "o">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"The breakpoint is deleted the first time it causes a stop.">;
+ def breakpoint_modify_thread_index
+ : Option<"thread-index", "x">,
+ Group<1>,
+ Arg<"ThreadIndex">,
+ Desc<"The breakpoint stops only for the thread whose inde${x} matches "
+ "this argument.">;
+ def breakpoint_modify_thread_id
+ : Option<"thread-id", "t">,
+ Group<1>,
+ Arg<"ThreadID">,
+ Desc<"The breakpoint stops only for the ${t}hread whose TID matches "
+ "this argument. The token 'current' resolves to the current "
+ "thread's ID.">;
+ def breakpoint_modify_thread_name
+ : Option<"thread-name", "T">,
+ Group<1>,
+ Arg<"ThreadName">,
+ Desc<"The breakpoint stops only for the ${t}hread whose thread name "
+ "matches this argument.">;
+ def breakpoint_modify_queue_name
+ : Option<"queue-name", "q">,
+ Group<1>,
+ Arg<"QueueName">,
+ Desc<"The breakpoint stops only for threads in the ${q}ueue whose name "
+ "is given by this argument.">;
+ def breakpoint_modify_condition
+ : Option<"condition", "c">,
+ Group<1>,
+ Arg<"Expression">,
+ Desc<"The breakpoint stops only if this ${c}ondition expression "
+ "evaluates to true.">;
def breakpoint_modify_condition_language
: Option<"condition-language", "Y">,
Group<1>,
Arg<"Language">,
Desc<"Specifies the Language to use when executing the breakpoint's "
"condition expression.">;
- def breakpoint_modify_auto_continue : Option<"auto-continue", "G">, Group<1>,
- Arg<"Boolean">,
- Desc<"The breakpoint will auto-continue after running its commands.">;
- def breakpoint_modify_enable : Option<"enable", "e">, Group<2>,
- Desc<"Enable the breakpoint.">;
- def breakpoint_modify_disable : Option<"disable", "d">, Group<3>,
- Desc<"Disable the breakpoint.">;
- def breakpoint_modify_command : Option<"command", "C">, Group<4>,
- Arg<"Command">,
- Desc<"A command to run when the breakpoint is hit, can be provided more "
- "than once, the commands will be run in left-to-right order.">;
+ def breakpoint_modify_auto_continue
+ : Option<"auto-continue", "G">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"The breakpoint will auto-continue after running its commands.">;
+ def breakpoint_modify_enable : Option<"enable", "e">,
+ Group<2>,
+ Desc<"${E}nable the breakpoint.">;
+ def breakpoint_modify_disable : Option<"disable", "d">,
+ Group<3>,
+ Desc<"${D}isable the breakpoint.">;
+ def breakpoint_modify_command
+ : Option<"command", "C">,
+ Group<4>,
+ Arg<"Command">,
+ Desc<
+ "A ${c}ommand to run when the breakpoint is hit, can be provided "
+ "more than once, the commands will be run in left-to-right order.">;
}
let Command = "breakpoint dummy" in {
- def breakpoint_dummy_options_dummy_breakpoints :
- Option<"dummy-breakpoints", "D">, Group<1>,
- Desc<"Act on Dummy breakpoints - i.e. breakpoints set before a file is "
- "provided, which prime new targets.">;
+ def breakpoint_dummy_options_dummy_breakpoints
+ : Option<"dummy-breakpoints", "D">,
+ Group<1>,
+ Desc<"Act on ${D}ummy breakpoints - i.e. breakpoints set before a file "
+ "is provided, which prime new targets.">;
}
let Command = "breakpoint set" in {
- def breakpoint_set_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
- Completion<"Module">, Groups<[1,2,3,4,5,6,7,8,9,11,12]>, // *not* in group 10
- Desc<"Set the breakpoint only in this shared library. Can repeat this "
- "option multiple times to specify multiple shared libraries.">;
- def breakpoint_set_hardware : Option<"hardware", "H">,
- Desc<"Require the breakpoint to use hardware breakpoints.">;
- def breakpoint_set_file : Option<"file", "f">, Arg<"Filename">,
- Completion<"SourceFile">, Groups<[1,3,4,5,6,7,8,9,11]>,
- Desc<"Specifies the source file in which to set this breakpoint. Note, by "
- "default lldb only looks for files that are #included if they use the "
- "standard include file extensions. To set breakpoints on .c/.cpp/.m/.mm "
- "files that are #included, set target.inline-breakpoint-strategy to "
- "\"always\".">;
- def breakpoint_set_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
- Required,
- Desc<"Specifies the line number on which to set this breakpoint.">;
- def breakpoint_set_column : Option<"column", "u">, Group<1>, Arg<"ColumnNum">,
- Desc<"Specifies the column number on which to set this breakpoint.">;
- def breakpoint_set_address : Option<"address", "a">, Group<2>,
- Arg<"AddressOrExpression">, Required,
- Desc<"Set the breakpoint at the specified address. If the address maps "
- "uniquely to a particular binary, then the address will be converted to "
- "a \"file\"address, so that the breakpoint will track that binary+offset "
- "no matter where the binary eventually loads. Alternately, if you also "
- "specify the module - with the -s option - then the address will be "
- "treated as a file address in that module, and resolved accordingly. "
- "Again, this will allow lldb to track that offset on subsequent reloads. "
- "The module need not have been loaded at the time you specify this "
- "breakpoint, and will get resolved when the module is loaded.">;
- def breakpoint_set_name : Option<"name", "n">, Group<3>, Arg<"FunctionName">,
- Completion<"Symbol">, Required,
- Desc<"Set the breakpoint by function name. Can be repeated multiple times "
- "to make one breakpoint for multiple names.">;
- def breakpoint_set_source_regexp_function :
- Option<"source-regexp-function", "X">, Group<9>, Arg<"FunctionName">,
- Completion<"Symbol">,
- Desc<"When used with '-p' limits the source regex to source contained in "
- "the named functions. Can be repeated multiple times.">;
- def breakpoint_set_fullname : Option<"fullname", "F">, Group<4>,
- Arg<"FullName">, Required, Completion<"Symbol">,
- Desc<"Set the breakpoint by fully qualified function names. For C++ this "
- "means namespaces and all arguments, and for Objective-C this means a full "
- "function prototype with class and selector. Can be repeated multiple times"
- " to make one breakpoint for multiple names.">;
- def breakpoint_set_selector : Option<"selector", "S">, Group<5>,
- Arg<"Selector">, Required,
- Desc<"Set the breakpoint by Objective-C selector name. Can be repeated "
- "multiple times to make one breakpoint for multiple Selectors.">;
- def breakpoint_set_method : Option<"method", "M">, Group<6>, Arg<"Method">,
- Required, Desc<"Set the breakpoint by C++ method names. Can be repeated "
- "multiple times to make one breakpoint for multiple methods.">;
- def breakpoint_set_func_regex : Option<"func-regex", "r">, Group<7>,
- Arg<"RegularExpression">, Required, Desc<"Set the breakpoint by function "
- "name, evaluating a regular-expression to find the function name(s).">;
- def breakpoint_set_basename : Option<"basename", "b">, Group<8>,
- Arg<"FunctionName">, Required, Completion<"Symbol">,
- Desc<"Set the breakpoint by function basename (C++ namespaces and arguments"
- " will be ignored). Can be repeated multiple times to make one breakpoint "
- "for multiple symbols.">;
- def breakpoint_set_source_pattern_regexp :
- Option<"source-pattern-regexp", "p">, Group<9>, Arg<"RegularExpression">,
- Required, Desc<"Set the breakpoint by specifying a regular expression which"
- " is matched against the source text in a source file or files specified "
- "with the -f can be specified more than once. If no source files "
- "are specified, uses the current \"default source file\". If you want to "
- "match against all source files, pass the \"--all-files\" option.">;
- def breakpoint_set_all_files : Option<"all-files", "A">, Group<9>,
- Desc<"All files are searched for source pattern matches.">;
- def breakpoint_set_language_exception : Option<"language-exception", "E">,
- Group<10>, Arg<"Language">, Required,
- Desc<"Set the breakpoint on exceptions thrown by the specified language "
- "(without options, on throw but not catch.)">;
- def breakpoint_set_on_throw : Option<"on-throw", "w">, Group<10>,
- Arg<"Boolean">, Desc<"Set the breakpoint on exception throW.">;
- def breakpoint_set_on_catch : Option<"on-catch", "h">, Group<10>,
- Arg<"Boolean">, Desc<"Set the breakpoint on exception catcH.">;
- def breakpoint_set_language : Option<"language", "L">, GroupRange<3, 8>,
- Arg<"Language">,
- Desc<"Specifies the Language to use when interpreting the breakpoint's "
- "expression (note: currently only implemented for setting breakpoints on "
- "identifiers). If not set the target.language setting is used.">;
- def breakpoint_set_skip_prologue : Option<"skip-prologue", "K">,
- Arg<"Boolean">, Groups<[1,3,4,5,6,7,8,12]>,
- Desc<"Skip the prologue if the breakpoint is at the beginning of a "
- "function. If not set the target.skip-prologue setting is used.">;
- def breakpoint_set_breakpoint_name : Option<"breakpoint-name", "N">,
- Arg<"BreakpointName">,
- Desc<"Adds this to the list of names for this breakpoint.">;
- def breakpoint_set_address_slide : Option<"address-slide", "R">,
- Arg<"Address">, Groups<[1,3,4,5,6,7,8,12]>,
- Desc<"Add the specified offset to whatever address(es) the breakpoint "
- "resolves to. At present this applies the offset directly as given, and "
- "doesn't try to align it to instruction boundaries.">;
- def breakpoint_set_move_to_nearest_code : Option<"move-to-nearest-code", "m">,
- Groups<[1,9,12]>, Arg<"Boolean">,
- Desc<"Move breakpoints to nearest code. If not set the "
- "target.move-to-nearest-code setting is used.">;
- def breakpoint_set_file_colon_line : Option<"joint-specifier", "y">, Group<12>, Arg<"FileLineColumn">,
- Required, Completion<"SourceFile">,
- Desc<"A specifier in the form filename:line[:column] for setting file & line breakpoints.">;
+ def breakpoint_set_shlib
+ : Option<"shlib", "s">,
+ Arg<"ShlibName">,
+ Completion<"Module">,
+ Groups<[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12]>, // *not* in group 10
+ Desc<
+ "${S}et the breakpoint only in this shared library. Can repeat "
+ "this option multiple times to specify multiple shared libraries.">;
+ def breakpoint_set_hardware
+ : Option<"hardware", "H">,
+ Desc<"Require the breakpoint to use ${h}ardware breakpoints.">;
+ def breakpoint_set_file
+ : Option<"file", "f">,
+ Arg<"Filename">,
+ Completion<"SourceFile">,
+ Groups<[1, 3, 4, 5, 6, 7, 8, 9, 11]>,
+ Desc<"Specifies the source ${f}ile in which to set this breakpoint. "
+ "Note, by default lldb only looks for files that are #included if "
+ "they use the standard include file extensions. To set "
+ "breakpoints on .c/.cpp/.m/.mm files that are #included, set "
+ "target.inline-breakpoint-strategy to \"always\".">;
+ def breakpoint_set_line
+ : Option<"line", "l">,
+ Group<1>,
+ Arg<"LineNum">,
+ Required,
+ Desc<"Specifies the ${l}ine number on which to set this breakpoint.">;
+ def breakpoint_set_column
+ : Option<"column", "u">,
+ Group<1>,
+ Arg<"ColumnNum">,
+ Desc<"Specifies the col${u}mn number on which to set this breakpoint.">;
+ def breakpoint_set_address
+ : Option<"address", "a">,
+ Group<2>,
+ Arg<"AddressOrExpression">,
+ Required,
+ Desc<"Set the breakpoint at the specified ${a}ddress. If the address "
+ "maps uniquely to a particular binary, then the address will be "
+ "converted to a \"file\"address, so that the breakpoint will "
+ "track that binary+offset no matter where the binary eventually "
+ "loads. Alternately, if you also specify the module - with the "
+ "-s option - then the address will be treated as a file address "
+ "in that module, and resolved accordingly. Again, this will "
+ "allow lldb to track that offset on subsequent reloads. The "
+ "module need not have been loaded at the time you specify this "
+ "breakpoint, and will get resolved when the module is loaded.">;
+ def breakpoint_set_name
+ : Option<"name", "n">,
+ Group<3>,
+ Arg<"FunctionName">,
+ Completion<"Symbol">,
+ Required,
+ Desc<"Set the breakpoint by function ${n}ame. Can be repeated "
+ "multiple times to make one breakpoint for multiple names.">;
+ def breakpoint_set_source_regexp_function
+ : Option<"source-regexp-function", "X">,
+ Group<9>,
+ Arg<"FunctionName">,
+ Completion<"Symbol">,
+ Desc<"When used with '-p' limits the source regex to source contained "
+ "in the named functions. Can be repeated multiple times.">;
+ def breakpoint_set_fullname
+ : Option<"fullname", "F">,
+ Group<4>,
+ Arg<"FullName">,
+ Required,
+ Completion<"Symbol">,
+ Desc<"Set the breakpoint by ${f}ully qualified function names. For C++ "
+ "this means namespaces and all arguments, and for Objective-C "
+ "this means a full function prototype with class and selector. "
+ "Can be repeated multiple times to make one breakpoint for "
+ "multiple names.">;
+ def breakpoint_set_selector
+ : Option<"selector", "S">,
+ Group<5>,
+ Arg<"Selector">,
+ Required,
+ Desc<"Set the breakpoint by Objective-C ${s}elector name. Can be "
+ "repeated multiple times to make one breakpoint for multiple "
+ "Selectors.">;
+ def breakpoint_set_method
+ : Option<"method", "M">,
+ Group<6>,
+ Arg<"Method">,
+ Required,
+ Desc<"Set the breakpoint by C++ ${m}ethod names. Can be repeated "
+ "multiple times to make one breakpoint for multiple methods.">;
+ def breakpoint_set_func_regex
+ : Option<"func-regex", "r">,
+ Group<7>,
+ Arg<"RegularExpression">,
+ Required,
+ Desc<"Set the breakpoint by function name, evaluating a "
+ "${r}egular-expression to find the function name(s).">;
+ def breakpoint_set_basename
+ : Option<"basename", "b">,
+ Group<8>,
+ Arg<"FunctionName">,
+ Required,
+ Completion<"Symbol">,
+ Desc<"Set the breakpoint by function ${b}asename (C++ namespaces and "
+ "arguments will be ignored). Can be repeated multiple times to "
+ "make one breakpoint for multiple symbols.">;
+ def breakpoint_set_source_pattern_regexp
+ : Option<"source-pattern-regexp", "p">,
+ Group<9>,
+ Arg<"RegularExpression">,
+ Required,
+ Desc<"Set the breakpoint by specifying a regular expression which is "
+ "matched against the source text in a source file or files "
+ "specified with the -f can be specified more than once. If no "
+ "source files are specified, uses the current \"default source "
+ "file\". If you want to match against all source files, ${p}ass "
+ "the \"--all-files\" option.">;
+ def breakpoint_set_all_files
+ : Option<"all-files", "A">,
+ Group<9>,
+ Desc<"${A}ll files are searched for source pattern matches.">;
+ def breakpoint_set_language_exception
+ : Option<"language-exception", "E">,
+ Group<10>,
+ Arg<"Language">,
+ Required,
+ Desc<"Set the breakpoint on ${e}xceptions thrown by the specified "
+ "language (without options, on throw but not catch.)">;
+ def breakpoint_set_on_throw
+ : Option<"on-throw", "w">,
+ Group<10>,
+ Arg<"Boolean">,
+ Desc<"Set the breakpoint on exception thro${w}.">;
+ def breakpoint_set_on_catch
+ : Option<"on-catch", "h">,
+ Group<10>,
+ Arg<"Boolean">,
+ Desc<"Set the breakpoint on exception catc${h}.">;
+ def breakpoint_set_language
+ : Option<"language", "L">,
+ GroupRange<3, 8>,
+ Arg<"Language">,
+ Desc<"Specifies the ${L}anguage to use when interpreting the "
+ "breakpoint's expression (note: currently only implemented for "
+ "setting breakpoints on identifiers). If not set the "
+ "target.language setting is used.">;
+ def breakpoint_set_skip_prologue
+ : Option<"skip-prologue", "K">,
+ Arg<"Boolean">,
+ Groups<[1, 3, 4, 5, 6, 7, 8, 12]>,
+ Desc<"S${k}ip the prologue if the breakpoint is at the beginning of a "
+ "function. If not set the target.skip-prologue setting is used.">;
+ def breakpoint_set_breakpoint_name
+ : Option<"breakpoint-name", "N">,
+ Arg<"BreakpointName">,
+ Desc<"Adds this to the list of ${n}ames for this breakpoint.">;
+ def breakpoint_set_address_slide
+ : Option<"address-slide", "R">,
+ Arg<"Address">,
+ Groups<[1, 3, 4, 5, 6, 7, 8, 12]>,
+ Desc<"Add the specified offset to whatever address(es) the breakpoint "
+ "${r}esolves to. At present this applies the offset directly as "
+ "given, and doesn't try to align it to instruction boundaries.">;
+ def breakpoint_set_move_to_nearest_code
+ : Option<"move-to-nearest-code", "m">,
+ Groups<[1, 9, 12]>,
+ Arg<"Boolean">,
+ Desc<"${M}ove breakpoints to nearest code. If not set the "
+ "target.move-to-nearest-code setting is used.">;
+ def breakpoint_set_file_colon_line
+ : Option<"joint-specifier", "y">,
+ Group<12>,
+ Arg<"FileLineColumn">,
+ Required,
+ Completion<"SourceFile">,
+ Desc<"A specifier in the form filename:line[:column] for setting file "
+ "& line breakpoints.">;
/* Don't add this option till it actually does something useful...
def breakpoint_set_exception_typename : Option<"exception-typename", "O">,
Arg<"TypeName">, Desc<"The breakpoint will only stop if an "
@@ -236,465 +358,743 @@ let Command = "breakpoint set" in {
}
let Command = "breakpoint clear" in {
- def breakpoint_clear_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Completion<"SourceFile">,
- Desc<"Specify the breakpoint by source location in this particular file.">;
- def breakpoint_clear_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
- Required,
- Desc<"Specify the breakpoint by source location at this particular line.">;
+ def breakpoint_clear_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Completion<"SourceFile">,
+ Desc<"Specify the breakpoint by source location "
+ "in this particular ${f}ile.">;
+ def breakpoint_clear_line : Option<"line", "l">,
+ Group<1>,
+ Arg<"LineNum">,
+ Required,
+ Desc<"Specify the breakpoint by source location "
+ "at this particular ${l}ine.">;
}
let Command = "breakpoint delete" in {
- def breakpoint_delete_force : Option<"force", "f">, Group<1>,
- Desc<"Delete all breakpoints without querying for confirmation.">;
- def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
- Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a "
- "file is provided, which prime new targets.">;
- def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>,
- Desc<"Delete all breakpoints which are currently disabled. When using the disabled option "
- "any breakpoints listed on the command line are EXCLUDED from deletion.">;
+ def breakpoint_delete_force
+ : Option<"force", "f">,
+ Group<1>,
+ Desc<"Delete all breakpoints without querying for confirmation.">;
+ def breakpoint_delete_dummy_breakpoints
+ : Option<"dummy-breakpoints", "D">,
+ Group<1>,
+ Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
+ def breakpoint_delete_disabled
+ : Option<"disabled", "d">,
+ Group<1>,
+ Desc<"${D}elete all breakpoints which are currently disabled. When "
+ "using the disabled option any breakpoints listed on the command "
+ "line are EXCLUDED from deletion.">;
}
let Command = "breakpoint name" in {
- def breakpoint_name_name : Option<"name", "N">, Group<1>,
- Arg<"BreakpointName">, Desc<"Specifies a breakpoint name to use.">;
- def breakpoint_name_breakpoint_id : Option<"breakpoint-id", "B">, Group<2>,
- Arg<"BreakpointID">, Desc<"Specify a breakpoint ID to use.">;
- def breakpoint_name_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
- Group<3>, Desc<"Operate on Dummy breakpoints - i.e. breakpoints set before "
- "a file is provided, which prime new targets.">;
- def breakpoint_name_help_string : Option<"help-string", "H">, Group<4>,
- Arg<"None">, Desc<"A help string describing the purpose of this name.">;
+ def breakpoint_name_name : Option<"name", "N">,
+ Group<1>,
+ Arg<"BreakpointName">,
+ Desc<"Specifies a breakpoint name to use.">;
+ def breakpoint_name_breakpoint_id : Option<"breakpoint-id", "B">,
+ Group<2>,
+ Arg<"BreakpointID">,
+ Desc<"Specify a breakpoint ID to use.">;
+ def breakpoint_name_dummy_breakpoints
+ : Option<"dummy-breakpoints", "D">,
+ Group<3>,
+ Desc<"Operate on Dummy breakpoints - i.e. breakpoints set before a "
+ "file is provided, which prime new targets.">;
+ def breakpoint_name_help_string
+ : Option<"help-string", "H">,
+ Group<4>,
+ Arg<"None">,
+ Desc<"A help string describing the purpose of this name.">;
}
let Command = "breakpoint access" in {
- def breakpoint_access_allow_list : Option<"allow-list", "L">, Group<1>,
- Arg<"Boolean">, Desc<"Determines whether the breakpoint will show up in "
- "break list if not referred to explicitly.">;
- def breakpoint_access_allow_disable : Option<"allow-disable", "A">, Group<2>,
- Arg<"Boolean">, Desc<"Determines whether the breakpoint can be disabled by "
- "name or when all breakpoints are disabled.">;
- def breakpoint_access_allow_delete : Option<"allow-delete", "D">, Group<3>,
- Arg<"Boolean">, Desc<"Determines whether the breakpoint can be deleted by "
- "name or when all breakpoints are deleted.">;
+ def breakpoint_access_allow_list
+ : Option<"allow-list", "L">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Determines whether the breakpoint will show up in break list if "
+ "not referred to explicitly.">;
+ def breakpoint_access_allow_disable
+ : Option<"allow-disable", "A">,
+ Group<2>,
+ Arg<"Boolean">,
+ Desc<"Determines whether the breakpoint can be disabled by name or "
+ "when all breakpoints are disabled.">;
+ def breakpoint_access_allow_delete
+ : Option<"allow-delete", "D">,
+ Group<3>,
+ Arg<"Boolean">,
+ Desc<"Determines whether the breakpoint can be deleted by name or when "
+ "all breakpoints are deleted.">;
}
let Command = "breakpoint read" in {
- def breakpoint_read_file : Option<"file", "f">, Arg<"Filename">, Required,
- Completion<"DiskFile">,
- Desc<"The file from which to read the breakpoints.">;
- def breakpoint_read_breakpoint_name : Option<"breakpoint-name", "N">,
- Arg<"BreakpointName">, Desc<"Only read in breakpoints with this name.">;
+ def breakpoint_read_file
+ : Option<"file", "f">,
+ Arg<"Filename">,
+ Required,
+ Completion<"DiskFile">,
+ Desc<"The ${f}ile from which to read the breakpoints.">;
+ def breakpoint_read_breakpoint_name
+ : Option<"breakpoint-name", "N">,
+ Arg<"BreakpointName">,
+ Desc<"Only read in breakpoints with this name.">;
}
let Command = "breakpoint write" in {
- def breakpoint_write_file : Option<"file", "f">, Arg<"Filename">, Required,
- Completion<"DiskFile">,
- Desc<"The file into which to write the breakpoints.">;
- def breakpoint_write_append : Option<"append", "a">,
- Desc<"Append to saved breakpoints file if it exists.">;
+ def breakpoint_write_file
+ : Option<"file", "f">,
+ Arg<"Filename">,
+ Required,
+ Completion<"DiskFile">,
+ Desc<"The ${f}ile into which to write the breakpoints.">;
+ def breakpoint_write_append
+ : Option<"append", "a">,
+ Desc<"${A}ppend to saved breakpoints file if it exists.">;
}
let Command = "breakpoint command add" in {
- def breakpoint_add_one_liner : Option<"one-liner", "o">, Group<1>,
- Arg<"OneLiner">, Desc<"Specify a one-line breakpoint command inline. Be "
- "sure to surround it with quotes.">;
- def breakpoint_add_stop_on_error : Option<"stop-on-error", "e">,
- Arg<"Boolean">, Desc<"Specify whether breakpoint command execution should "
- "terminate on error.">;
- def breakpoint_add_script_type : Option<"script-type", "s">,
- EnumArg<"ScriptLang">,
- Desc<"Specify the language for the commands - if none is specified, the "
- "lldb command interpreter will be used.">;
- def breakpoint_add_dummy_breakpoints : Option<"dummy-breakpoints", "D">,
- Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is "
- "provided, which prime new targets.">;
+ def breakpoint_add_one_liner
+ : Option<"one-liner", "o">,
+ Group<1>,
+ Arg<"OneLiner">,
+ Desc<"Specify a ${o}ne-line breakpoint command inline. Be sure to "
+ "surround it with quotes.">;
+ def breakpoint_add_stop_on_error
+ : Option<"stop-on-error", "e">,
+ Arg<"Boolean">,
+ Desc<"Specify whether breakpoint command execution should terminate on "
+ "error.">;
+ def breakpoint_add_script_type
+ : Option<"script-type", "s">,
+ EnumArg<"ScriptLang">,
+ Desc<"Specify the language for the commands - if none is specified, "
+ "the lldb command interpreter will be used.">;
+ def breakpoint_add_dummy_breakpoints
+ : Option<"dummy-breakpoints", "D">,
+ Desc<"Sets Dummy breakpoints - i.e. breakpoints set before a file is "
+ "provided, which prime new targets.">;
}
let Command = "breakpoint command delete" in {
- def breakpoint_command_delete_dummy_breakpoints :
- Option<"dummy-breakpoints", "D">, Group<1>,
- Desc<"Delete commands from Dummy breakpoints - i.e. breakpoints set before "
- "a file is provided, which prime new targets.">;
+ def breakpoint_command_delete_dummy_breakpoints
+ : Option<"dummy-breakpoints", "D">,
+ Group<1>,
+ Desc<"Delete commands from Dummy breakpoints - i.e. breakpoints set "
+ "before a file is provided, which prime new targets.">;
}
let Command = "disassemble" in {
- def disassemble_options_bytes : Option<"bytes", "b">,
- Desc<"Show opcode bytes when disassembling.">;
- def disassemble_options_kind : Option<"kind", "k">,
- Desc<"Show instruction control flow kind. Refer to the enum "
- "`InstructionControlFlowKind` for a list of control flow kind. "
- "As an important note, far jumps, far calls and far returns often indicate "
- "calls to and from kernel.">;
- def disassemble_options_context : Option<"context", "C">, Arg<"NumLines">,
- Desc<"Number of context lines of source to show.">;
- def disassemble_options_mixed : Option<"mixed", "m">,
- Desc<"Enable mixed source and assembly display.">;
- def disassemble_options_raw : Option<"raw", "r">,
- Desc<"Print raw disassembly with no symbol information.">;
- def disassemble_options_plugin : Option<"plugin", "P">, Arg<"Plugin">,
- Desc<"Name of the disassembler plugin you want to use.">;
- def disassemble_options_flavor : Option<"flavor", "F">,
- Arg<"DisassemblyFlavor">, Desc<"Name of the disassembly flavor you want to "
- "use. Currently the only valid options are default, and for Intel "
- "architectures, att and intel.">;
- def disassemble_options_cpu : Option<"cpu", "X">, Arg<"CPUName">,
- Desc<"Override the CPU for disassembling.">;
- def disassemble_options_features : Option<"features", "Y">, Arg<"CPUFeatures">,
- Desc<"Specify additional CPU features for disassembling.">;
- def disassemble_options_arch : Option<"arch", "A">, Arg<"Architecture">,
- Desc<"Specify the architecture to use from cross disassembly.">;
- def disassemble_options_start_address : Option<"start-address", "s">,
- Groups<[1,2]>, Arg<"AddressOrExpression">, Required,
- Desc<"Address at which to start disassembling.">;
- def disassemble_options_end_address : Option<"end-address", "e">, Group<1>,
- Arg<"AddressOrExpression">, Desc<"Address at which to end disassembling.">;
- def disassemble_options_count : Option<"count", "c">, Groups<[2,3,4,5,7]>,
- Arg<"NumLines">, Desc<"Number of instructions to display.">;
- def disassemble_options_name : Option<"name", "n">, Group<3>,
- Arg<"FunctionName">, Completion<"Symbol">,
- Desc<"Disassemble entire contents of the given function name.">;
- def disassemble_options_frame : Option<"frame", "f">, Group<4>,
- Desc<"Disassemble from the start of the current frame's function.">;
- def disassemble_options_pc : Option<"pc", "p">, Group<5>,
- Desc<"Disassemble around the current pc.">;
- def disassemble_options_line : Option<"line", "l">, Group<6>,
- Desc<"Disassemble the current frame's current source line instructions if "
- "there is debug line table information, else disassemble around the pc.">;
- def disassemble_options_address : Option<"address", "a">, Group<7>,
- Arg<"AddressOrExpression">,
- Desc<"Disassemble function containing this address.">;
- def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>,
- Desc<"Force disassembly of large functions.">;
+ def disassemble_options_bytes
+ : Option<"bytes", "b">,
+ Desc<"Show opcode ${b}ytes when disassembling.">;
+ def disassemble_options_kind
+ : Option<"kind", "k">,
+ Desc<"Show instruction control flow ${k}ind. Refer to the enum "
+ "`InstructionControlFlowKind` for a list of control flow kind. As "
+ "an important note, far jumps, far calls and far returns often "
+ "indicate calls to and from kernel.">;
+ def disassemble_options_context
+ : Option<"context", "C">,
+ Arg<"NumLines">,
+ Desc<"Number of ${c}ontext lines of source to show.">;
+ def disassemble_options_mixed
+ : Option<"mixed", "m">,
+ Desc<"Enable ${m}ixed source and assembly display.">;
+ def disassemble_options_raw
+ : Option<"raw", "r">,
+ Desc<"Print ${r}aw disassembly with no symbol information.">;
+ def disassemble_options_plugin
+ : Option<"plugin", "P">,
+ Arg<"Plugin">,
+ Desc<"Name of the disassembler ${p}lugin you want to use.">;
+ def disassemble_options_flavor
+ : Option<"flavor", "F">,
+ Arg<"DisassemblyFlavor">,
+ Desc<"Name of the disassembly ${f}lavor you want to use. Currently the "
+ "only valid options are default, and for Intel architectures, att "
+ "and intel.">;
+ def disassemble_options_cpu : Option<"cpu", "X">,
+ Arg<"CPUName">,
+ Desc<"Override the CPU for disassembling.">;
+ def disassemble_options_features
+ : Option<"features", "Y">,
+ Arg<"CPUFeatures">,
+ Desc<"Specify additional CPU features for disassembling.">;
+ def disassemble_options_arch
+ : Option<"arch", "A">,
+ Arg<"Architecture">,
+ Desc<"Specify the ${a}rchitecture to use for cross disassembly.">;
+ def disassemble_options_start_address
+ : Option<"start-address", "s">,
+ Groups<[1, 2]>,
+ Arg<"AddressOrExpression">,
+ Required,
+ Desc<"Address at which to ${s}tart disassembling.">;
+ def disassemble_options_end_address
+ : Option<"end-address", "e">,
+ Group<1>,
+ Arg<"AddressOrExpression">,
+ Desc<"Address at which to ${e}nd disassembling.">;
+ def disassemble_options_count : Option<"count", "c">,
+ Groups<[2, 3, 4, 5, 7]>,
+ Arg<"NumLines">,
+ Desc<"Number of instructions to display.">;
+ def disassemble_options_name
+ : Option<"name", "n">,
+ Group<3>,
+ Arg<"FunctionName">,
+ Completion<"Symbol">,
+ Desc<"Disassemble entire contents of the given function ${n}ame.">;
+ def disassemble_options_frame
+ : Option<"frame", "f">,
+ Group<4>,
+ Desc<"Disassemble from the start of the current frame's function.">;
+ def disassemble_options_pc : Option<"pc", "p">,
+ Group<5>,
+ Desc<"Disassemble around the current ${p}c.">;
+ def disassemble_options_line
+ : Option<"line", "l">,
+ Group<6>,
+ Desc<"Disassemble the current frame's current source ${l}ine "
+ "instructions if there is debug line table information, else "
+ "disassemble around the pc.">;
+ def disassemble_options_address
+ : Option<"address", "a">,
+ Group<7>,
+ Arg<"AddressOrExpression">,
+ Desc<"Disassemble function containing this ${a}ddress.">;
+ def disassemble_options_force : Option<"force", "\\x01">,
+ Groups<[2, 3, 4, 5, 7]>,
+ Desc<"Force disassembly of large functions.">;
+ def disassemble_options_variable
+ : Option<"variable", "v">,
+ Desc<"Enable ${v}ariable disassembly annotations for this invocation.">;
}
let Command = "diagnostics dump" in {
- def diagnostics_dump_directory : Option<"directory", "d">, Group<1>,
- Arg<"Path">, Desc<"Dump the diagnostics to the given directory.">;
+ def diagnostics_dump_directory
+ : Option<"directory", "d">,
+ Group<1>,
+ Arg<"Path">,
+ Desc<"Dump the diagnostics to the given directory.">;
}
let Command = "expression" in {
- def expression_options_all_threads : Option<"all-threads", "a">,
- Groups<[1,2]>, Arg<"Boolean">, Desc<"Should we run all threads if the "
- "execution doesn't complete on one thread.">;
- def expression_options_ignore_breakpoints : Option<"ignore-breakpoints", "i">,
- Groups<[1,2]>, Arg<"Boolean">,
- Desc<"Ignore breakpoint hits while running expressions">;
- def expression_options_timeout : Option<"timeout", "t">, Groups<[1,2]>,
- Arg<"UnsignedInteger">,
- Desc<"Timeout value (in microseconds) for running the expression.">;
- def expression_options_unwind_on_error : Option<"unwind-on-error", "u">,
- Groups<[1,2]>, Arg<"Boolean">,
- Desc<"Clean up program state if the expression causes a crash, or raises a "
- "signal. Note, unlike gdb hitting a breakpoint is controlled by another "
- "option (-i).">;
- def expression_options_debug : Option<"debug", "g">, Groups<[1,2]>,
- Desc<"When specified, debug the JIT code by setting a breakpoint on the "
- "first instruction and forcing breakpoints to not be ignored (-i0) and no "
- "unwinding to happen on error (-u0).">;
- def expression_options_language : Option<"language", "l">, Groups<[1,2,3]>,
- Arg<"Language">, Desc<"Specifies the Language to use when parsing the "
- "expression. If not set the target.language setting is used.">;
- def expression_options_apply_fixits : Option<"apply-fixits", "X">,
- Groups<[1,2]>, Arg<"Boolean">, Desc<"If true, simple fix-it hints will be "
- "automatically applied to the expression.">;
- def expression_options_description_verbosity :
- Option<"description-verbosity", "v">, Group<1>,
- OptionalEnumArg<"DescriptionVerbosity">,
- Desc<"How verbose should the output of this expression be, if the object "
- "description is asked for.">;
- def expression_options_top_level : Option<"top-level", "p">, Groups<[1,2]>,
- Desc<"Interpret the expression as a complete translation unit, without "
- "injecting it into the local context. Allows declaration of persistent, "
- "top-level entities without a $ prefix.">;
- def expression_options_allow_jit : Option<"allow-jit", "j">, Groups<[1,2]>,
- Arg<"Boolean">,
- Desc<"Controls whether the expression can fall back to being JITted if it's "
- "not supported by the interpreter (defaults to true).">;
- def persistent_result : Option<"persistent-result", "\\x01">, Groups<[1,2]>,
- Arg<"Boolean">,
- Desc<"Persist expression result in a variable for subsequent use. "
- "Expression results will be labeled with $-prefixed variables, e.g. $0, "
- "$1, etc.">;
+ def expression_options_all_threads
+ : Option<"all-threads", "a">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<
+ "Should we run ${a}ll threads if the execution doesn't complete on "
+ "one thread.">;
+ def expression_options_ignore_breakpoints
+ : Option<"ignore-breakpoints", "i">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<"${I}gnore breakpoint hits while running expressions">;
+ def expression_options_timeout
+ : Option<"timeout", "t">,
+ Groups<[1, 2]>,
+ Arg<"UnsignedInteger">,
+ Desc<"${T}imeout value (in microseconds) for running the expression.">;
+ def expression_options_unwind_on_error
+ : Option<"unwind-on-error", "u">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<"Clean up program state if the expression causes a crash, or "
+ "raises a signal. Note, unlike gdb hitting a breakpoint is "
+ "controlled by another option (-i).">;
+ def expression_options_debug
+ : Option<"debug", "g">,
+ Groups<[1, 2]>,
+ Desc<"When specified, debug the JIT code by setting a breakpoint on "
+ "the first instruction and forcing breakpoints to not be ignored "
+ "(-i0) and no unwinding to happen on error (-u0).">;
+ def expression_options_language
+ : Option<"language", "l">,
+ Groups<[1, 2, 3]>,
+ Arg<"Language">,
+ Desc<"Specifies the Language to use when parsing the expression. If "
+ "not set the target.language setting is used.">;
+ def expression_options_apply_fixits
+ : Option<"apply-fixits", "X">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<"If true, simple fix-it hints will be automatically applied to "
+ "the expression.">;
+ def expression_options_description_verbosity
+ : Option<"description-verbosity", "v">,
+ Group<1>,
+ OptionalEnumArg<"DescriptionVerbosity">,
+ Desc<"How verbose should the output of this expression be, if the "
+ "object description is asked for.">;
+ def expression_options_top_level
+ : Option<"top-level", "p">,
+ Groups<[1, 2]>,
+ Desc<"Interpret the expression as a complete translation unit, without "
+ "injecting it into the local context. Allows declaration of "
+ "persistent, top-level entities without a $ prefix.">;
+ def expression_options_allow_jit
+ : Option<"allow-jit", "j">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<"Controls whether the expression can fall back to being JITted if "
+ "it's not supported by the interpreter (defaults to true).">;
+ def persistent_result
+ : Option<"persistent-result", "\\x01">,
+ Groups<[1, 2]>,
+ Arg<"Boolean">,
+ Desc<"Persist expression result in a variable for subsequent use. "
+ "Expression results will be labeled with $-prefixed variables, "
+ "e.g. $0, $1, etc.">;
}
let Command = "frame diag" in {
- def frame_diag_register : Option<"register", "r">, Group<1>,
- Arg<"RegisterName">, Desc<"A register to diagnose.">;
- def frame_diag_address : Option<"address", "a">, Group<1>, Arg<"Address">,
- Desc<"An address to diagnose.">;
- def frame_diag_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">,
- Desc<"An optional offset. Requires --register.">;
+ def frame_diag_register : Option<"register", "r">,
+ Group<1>,
+ Arg<"RegisterName">,
+ Desc<"A register to diagnose.">;
+ def frame_diag_address : Option<"address", "a">,
+ Group<1>,
+ Arg<"Address">,
+ Desc<"An address to diagnose.">;
+ def frame_diag_offset : Option<"offset", "o">,
+ Group<1>,
+ Arg<"Offset">,
+ Desc<"An optional offset. Requires --register.">;
}
let Command = "frame select" in {
- def frame_select_relative : Option<"relative", "r">, Group<1>, Arg<"Offset">,
- Desc<"A relative frame index offset from the current frame index.">;
+ def frame_select_relative
+ : Option<"relative", "r">,
+ Group<1>,
+ Arg<"Offset">,
+ Desc<"A relative frame index offset from the current frame index.">;
}
let Command = "frame recognizer add" in {
- def frame_recognizer_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
- Completion<"Module">,
- Desc<"Name of the module or shared library that this recognizer applies "
- "to.">;
- def frame_recognizer_function : Option<"function", "n">, Arg<"Name">,
- Completion<"Symbol">,
- Desc<"Name of the function that this recognizer applies to. "
- "Can be specified more than once except if -x|--regex is provided.">;
- def frame_recognizer_python_class : Option<"python-class", "l">, Group<2>,
- Arg<"PythonClass">,
- Desc<"Give the name of a Python class to use for this frame recognizer.">;
- def frame_recognizer_regex : Option<"regex", "x">,
- Desc<"Function name and module name are actually regular expressions.">;
- def frame_recognizer_first_instruction_only : Option<"first-instruction-only", "f">, Arg<"Boolean">,
- Desc<"If true, only apply this recognizer to frames whose PC currently points to the "
- "first instruction of the specified function. If false, the recognizer "
- "will always be applied, regardless of the current position within the specified function. The "
- "implementer should keep in mind that some features, e.g. accessing function argument "
- "values via $arg<N>, are not guaranteed to work reliably in this case, so extra care must "
- "be taken to make the recognizer operate correctly. Defaults to true.">;
+ def frame_recognizer_shlib : Option<"shlib", "s">,
+ Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Name of the module or shared library that "
+ "this recognizer applies to.">;
+ def frame_recognizer_function
+ : Option<"function", "n">,
+ Arg<"Name">,
+ Completion<"Symbol">,
+ Desc<"Name of the function that this recognizer applies to. Can be "
+ "specified more than once except if -x|--regex is provided.">;
+ def frame_recognizer_python_class : Option<"python-class", "l">,
+ Group<2>,
+ Arg<"PythonClass">,
+ Desc<"Give the name of a Python class to "
+ "use for this frame recognizer.">;
+ def frame_recognizer_regex
+ : Option<"regex", "x">,
+ Desc<"Function name and module name are actually regular expressions.">;
+ def frame_recognizer_first_instruction_only
+ : Option<"first-instruction-only", "f">,
+ Arg<"Boolean">,
+ Desc<"If true, only apply this recognizer to frames whose PC currently "
+ "points to the first instruction of the specified function. If "
+ "false, the recognizer will always be applied, regardless of the "
+ "current position within the specified function. The implementer "
+ "should keep in mind that some features, e.g., accessing function "
+ "argument values via $arg<N>, are not guaranteed to work reliably "
+ "in this case, so extra care must be taken to make the recognizer "
+ "operate correctly. Defaults to true.">;
}
let Command = "history" in {
- def history_count : Option<"count", "c">, Group<1>, Arg<"UnsignedInteger">,
- Desc<"How many history commands to print.">;
- def history_start_index : Option<"start-index", "s">, Group<1>,
- Arg<"UnsignedInteger">, Desc<"Index at which to start printing history "
- "commands (or end to mean tail mode).">;
- def history_end_index : Option<"end-index", "e">, Group<1>,
- Arg<"UnsignedInteger">,
- Desc<"Index at which to stop printing history commands.">;
- def history_clear : Option<"clear", "C">, Group<2>,
- Desc<"Clears the current command history.">;
+ def history_count : Option<"count", "c">,
+ Group<1>,
+ Arg<"UnsignedInteger">,
+ Desc<"How many history commands to print.">;
+ def history_start_index : Option<"start-index", "s">,
+ Group<1>,
+ Arg<"UnsignedInteger">,
+ Desc<"Index at which to start printing history "
+ "commands (or end to mean tail mode).">;
+ def history_end_index
+ : Option<"end-index", "e">,
+ Group<1>,
+ Arg<"UnsignedInteger">,
+ Desc<"Index at which to stop printing history commands.">;
+ def history_clear : Option<"clear", "C">,
+ Group<2>,
+ Desc<"Clears the current command history.">;
}
let Command = "log enable" in {
- def log_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Desc<"Set the destination file to log to.">;
- def log_handler : Option<"log-handler", "h">, Group<1>,
- EnumArg<"LogHandler">, Desc<"Specify a log handler which determines where log messages are written.">;
- def log_buffer_size : Option<"buffer", "b">, Group<1>, Arg<"UnsignedInteger">,
- Desc<"Set the log to be buffered, using the specified buffer size, if supported by the log handler.">;
- def log_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Enable verbose logging.">;
- def log_sequence : Option<"sequence", "s">, Group<1>,
- Desc<"Prepend all log lines with an increasing integer sequence id.">;
- def log_timestamp : Option<"timestamp", "T">, Group<1>,
- Desc<"Prepend all log lines with a timestamp.">;
- def log_pid_tid : Option<"pid-tid", "p">, Group<1>,
- Desc<"Prepend all log lines with the process and thread ID that generates "
- "the log line.">;
- def log_thread_name : Option<"thread-name", "n">, Group<1>,
- Desc<"Prepend all log lines with the thread name for the thread that "
- "generates the log line.">;
-
- def log_stack : Option<"stack", "S">, Group<1>,
- Desc<"Append a stack backtrace to each log line.">;
- def log_append : Option<"append", "a">, Group<1>,
- Desc<"Append to the log file instead of overwriting.">;
- def log_file_function : Option<"file-function", "F">, Group<1>,
- Desc<"Prepend the names of files and function that generate the logs.">;
+ def log_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Set the destination file to log to.">;
+ def log_handler : Option<"log-handler", "h">,
+ Group<1>,
+ EnumArg<"LogHandler">,
+ Desc<"Specify a log handler which determines where log "
+ "messages are written.">;
+ def log_buffer_size : Option<"buffer", "b">,
+ Group<1>,
+ Arg<"UnsignedInteger">,
+ Desc<"Set the log to be buffered, using the specified "
+ "buffer size, if supported by the log handler.">;
+ def log_verbose : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Enable verbose logging.">;
+ def log_sequence
+ : Option<"sequence", "s">,
+ Group<1>,
+ Desc<"Prepend all log lines with an increasing integer sequence id.">;
+ def log_timestamp : Option<"timestamp", "T">,
+ Group<1>,
+ Desc<"Prepend all log lines with a timestamp.">;
+ def log_pid_tid : Option<"pid-tid", "p">,
+ Group<1>,
+ Desc<"Prepend all log lines with the process and thread ID "
+ "that generates the log line.">;
+ def log_thread_name : Option<"thread-name", "n">,
+ Group<1>,
+ Desc<"Prepend all log lines with the thread name for "
+ "the thread that generates the log line.">;
+
+ def log_stack : Option<"stack", "S">,
+ Group<1>,
+ Desc<"Append a stack backtrace to each log line.">;
+ def log_append : Option<"append", "a">,
+ Group<1>,
+ Desc<"Append to the log file instead of overwriting.">;
+ def log_file_function
+ : Option<"file-function", "F">,
+ Group<1>,
+ Desc<"Prepend the names of files and function that generate the logs.">;
}
let Command = "log dump" in {
- def log_dump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Desc<"Set the destination file to dump to.">;
+ def log_dump_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Set the destination file to dump to.">;
}
let Command = "memory read" in {
- def memory_read_num_per_line : Option<"num-per-line", "l">, Group<1>,
- Arg<"NumberPerLine">, Desc<"The number of items per line to display.">;
- def memory_read_binary : Option<"binary", "b">, Group<2>,
- Desc<"If true, memory will be saved as binary. If false, the memory is "
- "saved save as an ASCII dump that uses the format, size, count and number "
- "per line settings.">;
- def memory_read_type : Option<"type", "t">, Groups<[3,4]>, Arg<"Name">,
- Required, Desc<"The name of a type to view memory as.">;
- def memory_read_language : Option<"language", "x">, Group<4>, Arg<"Language">,
- Desc<"The language of the type to view memory as.">;
- def memory_read_offset : Option<"offset", "E">, Group<3>, Arg<"Count">,
- Desc<"How many elements of the specified type to skip before starting to "
- "display data.">;
- def memory_read_force : Option<"force", "r">, Groups<[1,2,3]>,
- Desc<"Necessary if reading over target.max-memory-read-size bytes.">;
+ def memory_read_num_per_line
+ : Option<"num-per-line", "l">,
+ Group<1>,
+ Arg<"NumberPerLine">,
+ Desc<"The number of items per line to display.">;
+ def memory_read_binary
+ : Option<"binary", "b">,
+ Group<2>,
+ Desc<"If true, memory will be saved as binary. If false, the memory is "
+ "saved save as an ASCII dump that uses the format, size, count "
+ "and number per line settings.">;
+ def memory_read_type : Option<"type", "t">,
+ Groups<[3, 4]>,
+ Arg<"Name">,
+ Required,
+ Desc<"The name of a type to view memory as.">;
+ def memory_read_language
+ : Option<"language", "x">,
+ Group<4>,
+ Arg<"Language">,
+ Desc<"The language of the type to view memory as.">;
+ def memory_read_offset : Option<"offset", "E">,
+ Group<3>,
+ Arg<"Count">,
+ Desc<"How many elements of the specified type to "
+ "skip before starting to display data.">;
+ def memory_read_force
+ : Option<"force", "r">,
+ Groups<[1, 2, 3]>,
+ Desc<"Necessary if reading over target.max-memory-read-size bytes.">;
}
let Command = "memory find" in {
- def memory_find_expression : Option<"expression", "e">, Group<1>,
- Arg<"Expression">, Required,
- Desc<"Evaluate an expression to obtain a byte pattern.">;
- def memory_find_string : Option<"string", "s">, Group<2>, Arg<"Name">,
- Required, Desc<"Use text to find a byte pattern.">;
- def memory_find_count : Option<"count", "c">, Arg<"Count">,
- Desc<"How many times to perform the search.">;
- def memory_find_dump_offset : Option<"dump-offset", "o">, Arg<"Offset">,
- Desc<"When dumping memory for a match, an offset from the match location to"
- " start dumping from.">;
+ def memory_find_expression
+ : Option<"expression", "e">,
+ Group<1>,
+ Arg<"Expression">,
+ Required,
+ Desc<"Evaluate an expression to obtain a byte pattern.">;
+ def memory_find_string : Option<"string", "s">,
+ Group<2>,
+ Arg<"Name">,
+ Required,
+ Desc<"Use text to find a byte pattern.">;
+ def memory_find_count : Option<"count", "c">,
+ Arg<"Count">,
+ Desc<"How many times to perform the search.">;
+ def memory_find_dump_offset
+ : Option<"dump-offset", "o">,
+ Arg<"Offset">,
+ Desc<"When dumping memory for a match, an offset from the match "
+ "location to start dumping from.">;
}
let Command = "memory write" in {
- def memory_write_infile : Option<"infile", "i">, Group<1>, Arg<"Filename">,
- Required, Desc<"Write memory using the contents of a file.">;
- def memory_write_offset : Option<"offset", "o">, Group<1>, Arg<"Offset">,
- Desc<"Start writing bytes from an offset within the input file.">;
+ def memory_write_infile : Option<"infile", "i">,
+ Group<1>,
+ Arg<"Filename">,
+ Required,
+ Desc<"Write memory using the contents of a file.">;
+ def memory_write_offset
+ : Option<"offset", "o">,
+ Group<1>,
+ Arg<"Offset">,
+ Desc<"Start writing bytes from an offset within the input file.">;
}
let Command = "memory region" in {
- def memory_region_all : Option<"all", "a">, Group<2>, Required,
- Desc<"Show all memory regions. This is equivalent to starting from address "
- "0 and repeating the command. Unmapped areas are included.">;
+ def memory_region_all : Option<"all", "a">,
+ Group<2>,
+ Required,
+ Desc<"Show all memory regions. This is equivalent to "
+ "starting from address 0 and repeating the "
+ "command. Unmapped areas are included.">;
}
let Command = "memory tag write" in {
- def memory_write_end_addr : Option<"end-addr", "e">, Group<1>,
- Arg<"AddressOrExpression">, Desc<
- "Set tags for start address to end-addr, repeating tags as needed"
- " to cover the range. (instead of calculating the range from the"
- " number of tags given)">;
+ def memory_write_end_addr
+ : Option<"end-addr", "e">,
+ Group<1>,
+ Arg<"AddressOrExpression">,
+ Desc<"Set tags for start address to end-addr, repeating tags as needed "
+ "to cover the range. (instead of calculating the range from the "
+ "number of tags given)">;
}
let Command = "register read" in {
- def register_read_alternate : Option<"alternate", "A">,
- Desc<"Display register names using the alternate register name if there "
- "is one.">;
- def register_read_set : Option<"set", "s">, Group<1>, Arg<"Index">,
- Desc<"Specify which register sets to dump by index.">;
- def register_read_all : Option<"all", "a">, Group<2>,
- Desc<"Show all register sets.">;
+ def register_read_alternate
+ : Option<"alternate", "A">,
+ Desc<"Display register names using the alternate register name if "
+ "there is one.">;
+ def register_read_set : Option<"set", "s">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"Specify which register sets to dump by index.">;
+ def register_read_all : Option<"all", "a">,
+ Group<2>,
+ Desc<"Show all register sets.">;
}
let Command = "source" in {
- def source_stop_on_error : Option<"stop-on-error", "e">, Arg<"Boolean">,
- Desc<"If true, stop executing commands on error.">;
- def source_stop_on_continue : Option<"stop-on-continue", "c">, Arg<"Boolean">,
- Desc<"If true, stop executing commands on continue.">;
- def source_silent_run : Option<"silent-run", "s">, Arg<"Boolean">,
- Desc<"If true don't echo commands while executing.">;
- def cmd_relative_to_command_file : Option<"relative-to-command-file", "C">,
- Desc<"Resolve non-absolute paths relative to the location of the "
- "current command file. This argument can only be used when the command is "
- "being sourced from a file.">;
+ def source_stop_on_error : Option<"stop-on-error", "e">,
+ Arg<"Boolean">,
+ Desc<"If true, stop executing commands on error.">;
+ def source_stop_on_continue
+ : Option<"stop-on-continue", "c">,
+ Arg<"Boolean">,
+ Desc<"If true, stop executing commands on continue.">;
+ def source_silent_run : Option<"silent-run", "s">,
+ Arg<"Boolean">,
+ Desc<"If true don't echo commands while executing.">;
+ def cmd_relative_to_command_file
+ : Option<"relative-to-command-file", "C">,
+ Desc<"Resolve non-absolute paths relative to the location of the "
+ "current command file. This argument can only be used when the "
+ "command is being sourced from a file.">;
}
let Command = "alias" in {
- def alias_help : Option<"help", "h">, Arg<"HelpText">,
- Desc<"Help text for this command">;
- def alias_long_help : Option<"long-help", "H">, Arg<"HelpText">,
- Desc<"Long help text for this command">;
+ def alias_help : Option<"help", "h">,
+ Arg<"HelpText">,
+ Desc<"Help text for this command">;
+ def alias_long_help : Option<"long-help", "H">,
+ Arg<"HelpText">,
+ Desc<"Long help text for this command">;
}
let Command = "regex" in {
- def regex_help : Option<"help", "h">, Group<1>, Arg<"None">,
- Desc<"The help text to display for this command.">;
- def regex_syntax : Option<"syntax", "s">, Group<1>, Arg<"None">,
- Desc<"A syntax string showing the typical usage syntax.">;
+ def regex_help : Option<"help", "h">,
+ Group<1>,
+ Arg<"None">,
+ Desc<"The help text to display for this command.">;
+ def regex_syntax : Option<"syntax", "s">,
+ Group<1>,
+ Arg<"None">,
+ Desc<"A syntax string showing the typical usage syntax.">;
}
let Command = "permissions" in {
- def permissions_permissions_value : Option<"permissions-value", "v">,
- Arg<"PermissionsNumber">,
- Desc<"Give out the numeric value for permissions (e.g. 757)">;
- def permissions_permissions_string : Option<"permissions-string", "s">,
- Arg<"PermissionsString">,
- Desc<"Give out the string value for permissions (e.g. rwxr-xr--).">;
+ def permissions_permissions_value
+ : Option<"permissions-value", "v">,
+ Arg<"PermissionsNumber">,
+ Desc<"Give out the numeric value for permissions (e.g. 757)">;
+ def permissions_permissions_string
+ : Option<"permissions-string", "s">,
+ Arg<"PermissionsString">,
+ Desc<"Give out the string value for permissions (e.g. rwxr-xr--).">;
def permissions_user_read : Option<"user-read", "r">,
- Desc<"Allow user to read.">;
+ Desc<"Allow user to read.">;
def permissions_user_write : Option<"user-write", "w">,
- Desc<"Allow user to write.">;
+ Desc<"Allow user to write.">;
def permissions_user_exec : Option<"user-exec", "x">,
- Desc<"Allow user to execute.">;
+ Desc<"Allow user to execute.">;
def permissions_group_read : Option<"group-read", "R">,
- Desc<"Allow group to read.">;
+ Desc<"Allow group to read.">;
def permissions_group_write : Option<"group-write", "W">,
- Desc<"Allow group to write.">;
+ Desc<"Allow group to write.">;
def permissions_group_exec : Option<"group-exec", "X">,
- Desc<"Allow group to execute.">;
+ Desc<"Allow group to execute.">;
def permissions_world_read : Option<"world-read", "d">,
- Desc<"Allow world to read.">;
+ Desc<"Allow world to read.">;
def permissions_world_write : Option<"world-write", "t">,
- Desc<"Allow world to write.">;
+ Desc<"Allow world to write.">;
def permissions_world_exec : Option<"world-exec", "e">,
- Desc<"Allow world to execute.">;
+ Desc<"Allow world to execute.">;
}
let Command = "platform fread" in {
- def platform_fread_offset : Option<"offset", "o">, Group<1>, Arg<"Index">,
- Desc<"Offset into the file at which to start reading.">;
- def platform_fread_count : Option<"count", "c">, Group<1>, Arg<"Count">,
- Desc<"Number of bytes to read from the file.">;
+ def platform_fread_offset
+ : Option<"offset", "o">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"Offset into the file at which to start reading.">;
+ def platform_fread_count : Option<"count", "c">,
+ Group<1>,
+ Arg<"Count">,
+ Desc<"Number of bytes to read from the file.">;
}
let Command = "platform fwrite" in {
- def platform_fwrite_offset : Option<"offset", "o">, Group<1>, Arg<"Index">,
- Desc<"Offset into the file at which to start reading.">;
- def platform_fwrite_data : Option<"data", "d">, Group<1>, Arg<"Value">,
- Desc<"Text to write to the file.">;
+ def platform_fwrite_offset
+ : Option<"offset", "o">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"Offset into the file at which to start reading.">;
+ def platform_fwrite_data : Option<"data", "d">,
+ Group<1>,
+ Arg<"Value">,
+ Desc<"Text to write to the file.">;
}
let Command = "platform process list" in {
- def platform_process_list_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
- Desc<"List the process info for a specific process ID.">;
- def platform_process_list_name : Option<"name", "n">, Group<2>,
- Arg<"ProcessName">, Required,
- Desc<"Find processes with executable basenames that match a string.">;
- def platform_process_list_ends_with : Option<"ends-with", "e">, Group<3>,
- Arg<"ProcessName">, Required,
- Desc<"Find processes with executable basenames that end with a string.">;
- def platform_process_list_starts_with : Option<"starts-with", "s">, Group<4>,
- Arg<"ProcessName">, Required,
- Desc<"Find processes with executable basenames that start with a string.">;
- def platform_process_list_contains : Option<"contains", "c">, Group<5>,
- Arg<"ProcessName">, Required,
- Desc<"Find processes with executable basenames that contain a string.">;
- def platform_process_list_regex : Option<"regex", "r">, Group<6>,
- Arg<"RegularExpression">, Required,
- Desc<"Find processes with executable basenames that match a regular "
- "expression.">;
- def platform_process_list_parent : Option<"parent", "P">, GroupRange<2, 6>,
- Arg<"Pid">, Desc<"Find processes that have a matching parent process ID.">;
- def platform_process_list_uid : Option<"uid", "u">, GroupRange<2, 6>,
- Arg<"UnsignedInteger">, Validator<"&posix_validator">,
- Desc<"Find processes that have a matching user ID.">;
- def platform_process_list_euid : Option<"euid", "U">, GroupRange<2, 6>,
- Arg<"UnsignedInteger">, Validator<"&posix_validator">,
- Desc<"Find processes that have a matching effective user ID.">;
- def platform_process_list_gid : Option<"gid", "g">, GroupRange<2, 6>,
- Arg<"UnsignedInteger">, Validator<"&posix_validator">,
- Desc<"Find processes that have a matching group ID.">;
- def platform_process_list_egid : Option<"egid", "G">, GroupRange<2, 6>,
- Arg<"UnsignedInteger">, Validator<"&posix_validator">,
- Desc<"Find processes that have a matching effective group ID.">;
- def platform_process_list_arch : Option<"arch", "a">, GroupRange<2, 6>,
- Arg<"Architecture">,
- Desc<"Find processes that have a matching architecture.">;
- def platform_process_list_show_args : Option<"show-args", "A">,
- GroupRange<1, 6>,
- Desc<"Show process arguments instead of the process executable basename.">;
- def platform_process_list_all_users: Option<"all-users", "x">,
- GroupRange<1,6>,
- Desc<"Show processes matching all user IDs.">;
- def platform_process_list_verbose : Option<"verbose", "v">, GroupRange<1, 6>,
- Desc<"Enable verbose output.">;
+ def platform_process_list_pid
+ : Option<"pid", "p">,
+ Group<1>,
+ Arg<"Pid">,
+ Desc<"List the process info for a specific process ID.">;
+ def platform_process_list_name
+ : Option<"name", "n">,
+ Group<2>,
+ Arg<"ProcessName">,
+ Required,
+ Desc<"Find processes with executable basenames that match a string.">;
+ def platform_process_list_ends_with
+ : Option<"ends-with", "e">,
+ Group<3>,
+ Arg<"ProcessName">,
+ Required,
+ Desc<
+ "Find processes with executable basenames that end with a string.">;
+ def platform_process_list_starts_with
+ : Option<"starts-with", "s">,
+ Group<4>,
+ Arg<"ProcessName">,
+ Required,
+ Desc<"Find processes with executable basenames that start with a "
+ "string.">;
+ def platform_process_list_contains
+ : Option<"contains", "c">,
+ Group<5>,
+ Arg<"ProcessName">,
+ Required,
+ Desc<"Find processes with executable basenames that contain a string.">;
+ def platform_process_list_regex
+ : Option<"regex", "r">,
+ Group<6>,
+ Arg<"RegularExpression">,
+ Required,
+ Desc<"Find processes with executable basenames that match a regular "
+ "expression.">;
+ def platform_process_list_parent
+ : Option<"parent", "P">,
+ GroupRange<2, 6>,
+ Arg<"Pid">,
+ Desc<"Find processes that have a matching parent process ID.">;
+ def platform_process_list_uid
+ : Option<"uid", "u">,
+ GroupRange<2, 6>,
+ Arg<"UnsignedInteger">,
+ Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching user ID.">;
+ def platform_process_list_euid
+ : Option<"euid", "U">,
+ GroupRange<2, 6>,
+ Arg<"UnsignedInteger">,
+ Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching effective user ID.">;
+ def platform_process_list_gid
+ : Option<"gid", "g">,
+ GroupRange<2, 6>,
+ Arg<"UnsignedInteger">,
+ Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching group ID.">;
+ def platform_process_list_egid
+ : Option<"egid", "G">,
+ GroupRange<2, 6>,
+ Arg<"UnsignedInteger">,
+ Validator<"&posix_validator">,
+ Desc<"Find processes that have a matching effective group ID.">;
+ def platform_process_list_arch
+ : Option<"arch", "a">,
+ GroupRange<2, 6>,
+ Arg<"Architecture">,
+ Desc<"Find processes that have a matching architecture.">;
+ def platform_process_list_show_args
+ : Option<"show-args", "A">,
+ GroupRange<1, 6>,
+ Desc<"Show process arguments instead of the process executable "
+ "basename.">;
+ def platform_process_list_all_users
+ : Option<"all-users", "x">,
+ GroupRange<1, 6>,
+ Desc<"Show processes matching all user IDs.">;
+ def platform_process_list_verbose : Option<"verbose", "v">,
+ GroupRange<1, 6>,
+ Desc<"Enable verbose output.">;
}
let Command = "platform process attach" in {
- def platform_process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">,
- Desc<"Name of the process plugin you want to use.">;
- def platform_process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
- Desc<"The process ID of an existing process to attach to.">;
- def platform_process_attach_name : Option<"name", "n">, Group<2>,
- Arg<"ProcessName">, Desc<"The name of the process to attach to.">;
- def platform_process_attach_waitfor : Option<"waitfor", "w">, Group<2>,
- Desc<"Wait for the process with <process-name> to launch.">;
+ def platform_process_attach_plugin
+ : Option<"plugin", "P">,
+ Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+ def platform_process_attach_pid
+ : Option<"pid", "p">,
+ Group<1>,
+ Arg<"Pid">,
+ Desc<"The process ID of an existing process to attach to.">;
+ def platform_process_attach_name
+ : Option<"name", "n">,
+ Group<2>,
+ Arg<"ProcessName">,
+ Desc<"The name of the process to attach to.">;
+ def platform_process_attach_waitfor
+ : Option<"waitfor", "w">,
+ Group<2>,
+ Desc<"Wait for the process with <process-name> to launch.">;
}
let Command = "platform shell" in {
- def platform_shell_host : Option<"host", "h">,
- Desc<"Run the commands on the host shell when enabled.">;
- def platform_shell_timeout : Option<"timeout", "t">, Arg<"Value">,
- Desc<"Seconds to wait for the remote host to finish running the command.">;
- def platform_shell_interpreter : Option<"shell", "s">, Arg<"Path">,
- Desc<"Shell interpreter path. This is the binary used to run the command.">;
+ def platform_shell_host
+ : Option<"host", "h">,
+ Desc<"Run the commands on the host shell when enabled.">;
+ def platform_shell_timeout : Option<"timeout", "t">,
+ Arg<"Value">,
+ Desc<"Seconds to wait for the remote host to "
+ "finish running the command.">;
+ def platform_shell_interpreter : Option<"shell", "s">,
+ Arg<"Path">,
+ Desc<"Shell interpreter path. This is the "
+ "binary used to run the command.">;
}
let Command = "plugin list" in {
@@ -703,808 +1103,1316 @@ let Command = "plugin list" in {
}
let Command = "process launch" in {
- def process_launch_stop_at_entry : Option<"stop-at-entry", "s">,
- Desc<"Stop at the entry point of the program when launching a process.">;
- def process_launch_stop_at_user_entry : Option<"stop-at-user-entry", "m">,
- Desc<"Stop at the user entry point when launching a process. For C based "
- "languages this will be the 'main' function, but this might differ for "
- "other languages.">;
- def process_launch_disable_aslr : Option<"disable-aslr", "A">, Arg<"Boolean">,
- Desc<"Set whether to disable address space layout randomization when launching a process.">;
- def process_launch_plugin : Option<"plugin", "P">, Arg<"Plugin">,
- Desc<"Name of the process plugin you want to use.">;
- def process_launch_working_dir : Option<"working-dir", "w">, Arg<"DirectoryName">,
- Desc<"Set the current working directory to <path> when running the inferior. This option "
- "applies only to the current `process launch` invocation. If "
- "`target.launch-working-dir` is set and this option is given, the value of this "
- "option will be used instead of the setting.">;
- def process_launch_arch : Option<"arch", "a">, Arg<"Architecture">,
- Desc<"Set the architecture for the process to launch when ambiguous.">;
- def process_launch_environment : Option<"environment", "E">,
- Arg<"None">, Desc<"Specify an environment variable name/value string "
- "(--environment NAME=VALUE). Can be specified multiple times for subsequent "
- "environment entries.">;
- def process_launch_shell : Option<"shell", "c">, GroupRange<1,3>,
- OptionalArg<"Filename">, Desc<"Run the process in a shell (not supported on all platforms).">;
- def process_launch_stdin : Option<"stdin", "i">, Group<1>,
- Arg<"Filename">, Desc<"Redirect stdin for the process to <filename>.">;
- def process_launch_stdout : Option<"stdout", "o">, Group<1>,
- Arg<"Filename">, Desc<"Redirect stdout for the process to <filename>.">;
- def process_launch_stderr : Option<"stderr", "e">, Group<1>,
- Arg<"Filename">, Desc<"Redirect stderr for the process to <filename>.">;
- def process_launch_tty : Option<"tty", "t">, Group<2>,
- Desc<"Start the process in a terminal (not supported on all platforms).">;
- def process_launch_no_stdio : Option<"no-stdio", "n">, Group<3>,
- Desc<"Do not set up for terminal I/O to go to running process.">;
- def process_launch_shell_expand_args : Option<"shell-expand-args", "X">, Group<4>,
- Arg<"Boolean">, Desc<"Set whether to shell expand arguments to the process when launching.">;
+ def process_launch_stop_at_entry
+ : Option<"stop-at-entry", "s">,
+ Desc<
+ "Stop at the entry point of the program when launching a process.">;
+ def process_launch_stop_at_user_entry
+ : Option<"stop-at-user-entry", "m">,
+ Desc<"Stop at the user entry point when launching a process. For C "
+ "based languages this will be the 'main' function, but this might "
+ "differ for other languages.">;
+ def process_launch_disable_aslr
+ : Option<"disable-aslr", "A">,
+ Arg<"Boolean">,
+ Desc<"Set whether to disable address space layout randomization when "
+ "launching a process.">;
+ def process_launch_plugin
+ : Option<"plugin", "P">,
+ Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
+ def process_launch_working_dir
+ : Option<"working-dir", "w">,
+ Arg<"DirectoryName">,
+ Desc<"Set the current working directory to <path> when running the "
+ "inferior. This option applies only to the current `process "
+ "launch` invocation. If `target.launch-working-dir` is set and "
+ "this option is given, the value of this option will be used "
+ "instead of the setting.">;
+ def process_launch_arch
+ : Option<"arch", "a">,
+ Arg<"Architecture">,
+ Desc<"Set the architecture for the process to launch when ambiguous.">;
+ def process_launch_environment
+ : Option<"environment", "E">,
+ Arg<"None">,
+ Desc<"Specify an environment variable name/value string (--environment "
+ "NAME=VALUE). Can be specified multiple times for subsequent "
+ "environment entries.">;
+ def process_launch_shell
+ : Option<"shell", "c">,
+ GroupRange<1, 3>,
+ OptionalArg<"Filename">,
+ Desc<"Run the process in a shell (not supported on all platforms).">;
+ def process_launch_stdin
+ : Option<"stdin", "i">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Redirect stdin for the process to <filename>.">;
+ def process_launch_stdout
+ : Option<"stdout", "o">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Redirect stdout for the process to <filename>.">;
+ def process_launch_stderr
+ : Option<"stderr", "e">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Redirect stderr for the process to <filename>.">;
+ def process_launch_tty : Option<"tty", "t">,
+ Group<2>,
+ Desc<"Start the process in a terminal (not "
+ "supported on all platforms).">;
+ def process_launch_no_stdio
+ : Option<"no-stdio", "n">,
+ Group<3>,
+ Desc<"Do not set up for terminal I/O to go to running process.">;
+ def process_launch_shell_expand_args
+ : Option<"shell-expand-args", "X">,
+ Group<4>,
+ Arg<"Boolean">,
+ Desc<"Set whether to shell expand arguments to the process when "
+ "launching.">;
}
let Command = "process attach" in {
- def process_attach_continue : Option<"continue", "c">,
- Desc<"Immediately continue the process once attached.">;
- def process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">,
- Desc<"Name of the process plugin you want to use.">;
- def process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">,
- Desc<"The process ID of an existing process to attach to.">;
- def process_attach_name : Option<"name", "n">, Group<2>, Arg<"ProcessName">,
- Desc<"The name of the process to attach to.">;
- def process_attach_include_existing : Option<"include-existing", "i">,
- Group<2>, Desc<"Include existing processes when doing attach -w.">;
- def process_attach_waitfor : Option<"waitfor", "w">, Group<2>,
- Desc<"Wait for the process with <process-name> to launch.">;
+ def process_attach_continue
+ : Option<"continue", "c">,
+ Desc<"Immediately ${c}ontinue the process once attached.">;
+ def process_attach_plugin
+ : Option<"plugin", "P">,
+ Arg<"Plugin">,
+ Desc<"Name of the process ${p}lugin you want to use.">;
+ def process_attach_pid
+ : Option<"pid", "p">,
+ Group<1>,
+ Arg<"Pid">,
+ Desc<"The ${p}rocess ID of an existing process to attach to.">;
+ def process_attach_name : Option<"name", "n">,
+ Group<2>,
+ Arg<"ProcessName">,
+ Desc<"The ${n}ame of the process to attach to.">;
+ def process_attach_include_existing
+ : Option<"include-existing", "i">,
+ Group<2>,
+ Desc<"${I}nclude existing processes when doing attach -w.">;
+ def process_attach_waitfor
+ : Option<"waitfor", "w">,
+ Group<2>,
+ Desc<"${W}ait for the process with <process-name> to launch.">;
}
let Command = "process continue" in {
- def process_continue_ignore_count : Option<"ignore-count", "i">, Groups<[1,2]>,
- Arg<"UnsignedInteger">, Desc<"Ignore <N> crossings of the breakpoint (if it"
- " exists) for the currently selected thread.">;
- def process_continue_run_to_bkpt : Option<"continue-to-bkpt", "b">, Groups<[3,4]>,
- Arg<"BreakpointIDRange">, Desc<"Specify a breakpoint to continue to, temporarily "
- "ignoring other breakpoints. Can be specified more than once. "
- "The continue action will be done synchronously if this option is specified.">;
- def thread_continue_forward : Option<"forward", "F">, Groups<[1,3]>,
- Desc<"Set the direction to forward before continuing.">;
- def thread_continue_reverse : Option<"reverse", "R">, Groups<[2,4]>,
- Desc<"Set the direction to reverse before continuing.">;
+ def process_continue_ignore_count
+ : Option<"ignore-count", "i">,
+ Groups<[1, 2]>,
+ Arg<"UnsignedInteger">,
+ Desc<"Ignore <N> crossings of the breakpoint (if it exists) for the "
+ "currently selected thread.">;
+ def process_continue_run_to_bkpt
+ : Option<"continue-to-bkpt", "b">,
+ Groups<[3, 4]>,
+ Arg<"BreakpointIDRange">,
+ Desc<"Specify a breakpoint to continue to, temporarily ignoring other "
+ "breakpoints. Can be specified more than once. The continue "
+ "action will be done synchronously if this option is specified.">;
+ def thread_continue_forward
+ : Option<"forward", "F">,
+ Groups<[1, 3]>,
+ Desc<"Set the direction to forward before continuing.">;
+ def thread_continue_reverse
+ : Option<"reverse", "R">,
+ Groups<[2, 4]>,
+ Desc<"Set the direction to reverse before continuing.">;
}
let Command = "process detach" in {
- def process_detach_keep_stopped : Option<"keep-stopped", "s">, Group<1>,
- Arg<"Boolean">, Desc<"Whether or not the process should be kept stopped on"
- " detach (if possible).">;
+ def process_detach_keep_stopped
+ : Option<"keep-stopped", "s">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Whether or not the process should be kept stopped on"
+ " detach (if possible).">;
}
let Command = "process connect" in {
- def process_connect_plugin : Option<"plugin", "p">, Arg<"Plugin">,
- Desc<"Name of the process plugin you want to use.">;
+ def process_connect_plugin
+ : Option<"plugin", "p">,
+ Arg<"Plugin">,
+ Desc<"Name of the process plugin you want to use.">;
}
let Command = "process load" in {
- def process_load_install : Option<"install", "i">, OptionalArg<"Path">,
- Desc<"Install the shared library to the target. If specified without an "
- "argument then the library will installed in the current working "
- "directory.">;
+ def process_load_install
+ : Option<"install", "i">,
+ OptionalArg<"Path">,
+ Desc<
+ "Install the shared library to the target. If specified without an "
+ "argument then the library will installed in the current working "
+ "directory.">;
}
let Command = "process handle" in {
- def process_handle_clear : Option<"clear", "c">, Group<2>,
- Desc<"Removes the signals listed from the Target signal handlers">;
- def process_handle_stop : Option<"stop", "s">, Group<1>, Arg<"Boolean">,
- Desc<"Whether or not the process should be stopped if the signal is "
- "received.">;
- def process_handle_notify : Option<"notify", "n">, Group<1>, Arg<"Boolean">,
- Desc<"Whether or not the debugger should notify the user if the signal is "
- "received.">;
- def process_handle_pass : Option<"pass", "p">, Group<1>, Arg<"Boolean">,
- Desc<"Whether or not the signal should be passed to the process.">;
- def process_handle_only_target : Option<"target", "t">, Group<1>,
- Desc<"Show only the signals with behaviors modified in this target">;
- def process_handle_dummy : Option<"dummy", "d">, Group<2>,
- Desc<"Also clear the values in the dummy target so they won't be inherited by new targets.">;
+ def process_handle_clear
+ : Option<"clear", "c">,
+ Group<2>,
+ Desc<"Removes the signals listed from the Target signal handlers">;
+ def process_handle_stop
+ : Option<"stop", "s">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Whether or not the process should be stopped if the signal is "
+ "received.">;
+ def process_handle_notify : Option<"notify", "n">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Whether or not the debugger should notify "
+ "the user if the signal is "
+ "received.">;
+ def process_handle_pass
+ : Option<"pass", "p">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Whether or not the signal should be passed to the process.">;
+ def process_handle_only_target
+ : Option<"target", "t">,
+ Group<1>,
+ Desc<"Show only the signals with behaviors modified in this target">;
+ def process_handle_dummy : Option<"dummy", "d">,
+ Group<2>,
+ Desc<"Also clear the values in the dummy target "
+ "so they won't be inherited by new targets.">;
}
let Command = "process status" in {
- def process_status_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Show verbose process status including extended crash information.">;
- def process_status_dump : Option<"dump-modification-id", "d">, Group<1>,
- Desc<"Dump the state of the ProcessModID of the stopped process.">;
+ def process_status_verbose : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Show verbose process status including "
+ "extended crash information.">;
+ def process_status_dump
+ : Option<"dump-modification-id", "d">,
+ Group<1>,
+ Desc<"Dump the state of the ProcessModID of the stopped process.">;
}
let Command = "process save_core" in {
- def process_save_core_style : Option<"style", "s">, Group<1>,
- EnumArg<"SaveCoreStyle">, Desc<"Request a specific style "
- "of corefile to be saved.">;
- def process_save_core_plugin_name : Option<"plugin-name", "p">,
- OptionalArg<"Plugin">, Desc<"Specify a plugin name to create the core file. "
- "This allows core files to be saved in different formats.">;
+ def process_save_core_style : Option<"style", "s">,
+ Group<1>,
+ EnumArg<"SaveCoreStyle">,
+ Desc<"Request a specific style "
+ "of corefile to be saved.">;
+ def process_save_core_plugin_name
+ : Option<"plugin-name", "p">,
+ OptionalArg<"Plugin">,
+ Desc<"Specify a plugin name to create the core file. "
+ "This allows core files to be saved in different formats.">;
}
let Command = "script import" in {
- def script_import_allow_reload : Option<"allow-reload", "r">, Group<1>,
- Desc<"Allow the script to be loaded even if it was already loaded before. "
- "This argument exists for backwards compatibility, but reloading is always "
- "allowed, whether you specify it or not.">;
- def relative_to_command_file : Option<"relative-to-command-file", "c">,
- Group<1>, Desc<"Resolve non-absolute paths relative to the location of the "
- "current command file. This argument can only be used when the command is "
- "being sourced from a file.">;
- def silent : Option<"silent", "s">, Group<1>,
- Desc<"If true don't print any script output while importing.">;
+ def script_import_allow_reload
+ : Option<"allow-reload", "r">,
+ Group<1>,
+ Desc<"Allow the script to be loaded even if it was already loaded "
+ "before. "
+ "This argument exists for backwards compatibility, but reloading "
+ "is always "
+ "allowed, whether you specify it or not.">;
+ def relative_to_command_file
+ : Option<"relative-to-command-file", "c">,
+ Group<1>,
+ Desc<"Resolve non-absolute paths relative to the location of the "
+ "current command file. This argument can only be used when the "
+ "command is "
+ "being sourced from a file.">;
+ def silent : Option<"silent", "s">,
+ Group<1>,
+ Desc<"If true don't print any script output while importing.">;
}
let Command = "script add" in {
- def script_add_function : Option<"function", "f">, Group<1>,
- Arg<"PythonFunction">,
- Desc<"Name of the Python function to bind to this command name.">;
- def script_add_class : Option<"class", "c">, Groups<[2,3]>,
- Arg<"PythonClass">,
- Desc<"Name of the Python class to bind to this command name.">;
- def script_add_help : Option<"help", "h">, Group<1>, Arg<"HelpText">,
- Desc<"The help text to display for this command.">;
- def script_add_overwrite : Option<"overwrite", "o">,
- Desc<"Overwrite an existing command at this node.">;
- def script_add_synchronicity : Option<"synchronicity", "s">,
- EnumArg<"ScriptedCommandSynchronicity">,
- Desc<"Set the synchronicity of this command's executions with regard to "
- "LLDB event system.">;
- def script_add_completion_type : Option<"completion-type", "C">,
- Groups<[1,2]>, EnumArg<"CompletionType">,
- Desc<"Specify which completion type the command should use - if none is "
- "specified, the command won't use auto-completion.">;
- def script_add_parsed_command : Option<"parsed", "p">, Group<3>,
- Desc<"Make a parsed command. The command class will provide the command "
- "definition by implementing get_options and get_arguments.">;
-
+ def script_add_function
+ : Option<"function", "f">,
+ Group<1>,
+ Arg<"PythonFunction">,
+ Desc<"Name of the Python function to bind to this command name.">;
+ def script_add_class
+ : Option<"class", "c">,
+ Groups<[2, 3]>,
+ Arg<"PythonClass">,
+ Desc<"Name of the Python class to bind to this command name.">;
+ def script_add_help : Option<"help", "h">,
+ Group<1>,
+ Arg<"HelpText">,
+ Desc<"The help text to display for this command.">;
+ def script_add_overwrite
+ : Option<"overwrite", "o">,
+ Desc<"Overwrite an existing command at this node.">;
+ def script_add_synchronicity
+ : Option<"synchronicity", "s">,
+ EnumArg<"ScriptedCommandSynchronicity">,
+ Desc<
+ "Set the synchronicity of this command's executions with regard to "
+ "LLDB event system.">;
+ def script_add_completion_type
+ : Option<"completion-type", "C">,
+ Groups<[1, 2]>,
+ EnumArg<"CompletionType">,
+ Desc<
+ "Specify which completion type the command should use - if none is "
+ "specified, the command won't use auto-completion.">;
+ def script_add_parsed_command
+ : Option<"parsed", "p">,
+ Group<3>,
+ Desc<
+ "Make a parsed command. The command class will provide the command "
+ "definition by implementing get_options and get_arguments.">;
}
let Command = "container add" in {
- def container_add_help : Option<"help", "h">, Arg<"HelpText">,
- Desc<"Help text for this command">;
- def container_add_long_help : Option<"long-help", "H">, Arg<"HelpText">,
- Desc<"Long help text for this command">;
- def container_add_overwrite : Option<"overwrite", "o">, Group<1>,
- Desc<"Overwrite an existing command at this node.">;
+ def container_add_help : Option<"help", "h">,
+ Arg<"HelpText">,
+ Desc<"Help text for this command">;
+ def container_add_long_help : Option<"long-help", "H">,
+ Arg<"HelpText">,
+ Desc<"Long help text for this command">;
+ def container_add_overwrite
+ : Option<"overwrite", "o">,
+ Group<1>,
+ Desc<"Overwrite an existing command at this node.">;
}
let Command = "scripting run" in {
def script_language : Option<"language", "l">,
- EnumArg<"ScriptLang">, Desc<"Specify the scripting "
- " language. If none is specific the default scripting language is used.">;
+ EnumArg<"ScriptLang">,
+ Desc<"Specify the scripting "
+ " language. If none is specific the default "
+ "scripting language is used.">;
}
let Command = "scripting extension list" in {
- def scripting_extension_list_language : Option<"language", "l">,
- EnumArg<"ScriptLang">, Desc<"Specify the scripting "
- " language. If none is specified the default scripting language is used.">;
+ def scripting_extension_list_language
+ : Option<"language", "l">,
+ EnumArg<"ScriptLang">,
+ Desc<"Specify the scripting "
+ " language. If none is specified the default scripting language "
+ "is used.">;
}
let Command = "source info" in {
- def source_info_count : Option<"count", "c">, Arg<"Count">,
- Desc<"The number of line entries to display.">;
- def source_info_shlib : Option<"shlib", "s">, Groups<[1,2]>, Arg<"ShlibName">,
- Completion<"Module">, Desc<"Look up the source in the given module or "
- "shared library (can be specified more than once).">;
- def source_info_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Completion<"SourceFile">, Desc<"The file from which to display source.">;
- def source_info_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
- Desc<"The line number at which to start the displaying lines.">;
- def source_info_end_line : Option<"end-line", "e">, Group<1>, Arg<"LineNum">,
- Desc<"The line number at which to stop displaying lines.">;
- def source_info_name : Option<"name", "n">, Group<2>, Arg<"Symbol">,
- Completion<"Symbol">,
- Desc<"The name of a function whose source to display.">;
- def source_info_address : Option<"address", "a">, Group<3>,
- Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source"
- " information for the corresponding file and line.">;
+ def source_info_count : Option<"count", "c">,
+ Arg<"Count">,
+ Desc<"The number of line entries to display.">;
+ def source_info_shlib
+ : Option<"shlib", "s">,
+ Groups<[1, 2]>,
+ Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Look up the source in the given module or "
+ "shared library (can be specified more than once).">;
+ def source_info_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Completion<"SourceFile">,
+ Desc<"The file from which to display source.">;
+ def source_info_line
+ : Option<"line", "l">,
+ Group<1>,
+ Arg<"LineNum">,
+ Desc<"The line number at which to start the displaying lines.">;
+ def source_info_end_line
+ : Option<"end-line", "e">,
+ Group<1>,
+ Arg<"LineNum">,
+ Desc<"The line number at which to stop displaying lines.">;
+ def source_info_name
+ : Option<"name", "n">,
+ Group<2>,
+ Arg<"Symbol">,
+ Completion<"Symbol">,
+ Desc<"The name of a function whose source to display.">;
+ def source_info_address
+ : Option<"address", "a">,
+ Group<3>,
+ Arg<"AddressOrExpression">,
+ Desc<"Lookup the address and display the source"
+ " information for the corresponding file and line.">;
}
let Command = "source list" in {
- def source_list_count : Option<"count", "c">, Arg<"Count">,
- Desc<"The number of source lines to display.">;
- def source_list_shlib : Option<"shlib", "s">, Groups<[1,2,5]>, Arg<"ShlibName">,
- Completion<"Module">,
- Desc<"Look up the source file in the given shared library.">;
- def source_list_show_breakpoints : Option<"show-breakpoints", "b">,
- Desc<"Show the line table locations from the debug information that "
- "indicate valid places to set source level breakpoints.">;
- def source_list_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Completion<"SourceFile">, Desc<"The file from which to display source.">;
- def source_list_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
- Desc<"The line number at which to start the display source.">;
- def source_list_name : Option<"name", "n">, Group<2>, Arg<"Symbol">,
- Completion<"Symbol">,
- Desc<"The name of a function whose source to display.">;
- def source_list_address : Option<"address", "a">, Group<3>,
- Arg<"AddressOrExpression">, Desc<"Lookup the address and display the source"
- " information for the corresponding file and line.">;
- def source_list_reverse : Option<"reverse", "r">, Group<4>, Desc<"Reverse the"
- " listing to look backwards from the last displayed block of source.">;
- def source_list_file_colon_line : Option<"joint-specifier", "y">, Group<5>,
- Arg<"FileLineColumn">, Completion<"SourceFile">,
- Desc<"A specifier in the form filename:line[:column] from which to display"
- " source.">;
+ def source_list_count : Option<"count", "c">,
+ Arg<"Count">,
+ Desc<"The number of source lines to display.">;
+ def source_list_shlib
+ : Option<"shlib", "s">,
+ Groups<[1, 2, 5]>,
+ Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Look up the source file in the given shared library.">;
+ def source_list_show_breakpoints
+ : Option<"show-breakpoints", "b">,
+ Desc<"Show the line table locations from the debug information that "
+ "indicate valid places to set source level breakpoints.">;
+ def source_list_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Completion<"SourceFile">,
+ Desc<"The file from which to display source.">;
+ def source_list_line
+ : Option<"line", "l">,
+ Group<1>,
+ Arg<"LineNum">,
+ Desc<"The line number at which to start displaying source.">;
+ def source_list_name
+ : Option<"name", "n">,
+ Group<2>,
+ Arg<"Symbol">,
+ Completion<"Symbol">,
+ Desc<"The name of a function whose source to display.">;
+ def source_list_address
+ : Option<"address", "a">,
+ Group<3>,
+ Arg<"AddressOrExpression">,
+ Desc<"Lookup the address and display the source information for the "
+ "corresponding file and line.">;
+ def source_list_reverse : Option<"reverse", "r">,
+ Group<4>,
+ Desc<"Reverse the listing to look backwards from "
+ "the last displayed block of source.">;
+ def source_list_file_colon_line
+ : Option<"joint-specifier", "y">,
+ Group<5>,
+ Arg<"FileLineColumn">,
+ Completion<"SourceFile">,
+ Desc<"A specifier in the form filename:line[:column] from which to "
+ "display source.">;
}
let Command = "target dependents" in {
- def dependents_no_dependents : Option<"no-dependents", "d">, Group<1>,
- OptionalEnumArg<"Value">,
- Desc<"Whether or not to load dependents when creating a target. If the "
- "option is not specified, the value is implicitly 'default'. If the "
- "option is specified but without a value, the value is implicitly "
- "'true'.">;
+ def dependents_no_dependents
+ : Option<"no-dependents", "d">,
+ Group<1>,
+ OptionalEnumArg<"Value">,
+ Desc<"Whether or not to load dependents when creating a target. If the "
+ "option is not specified, the value is implicitly 'default'. If "
+ "the "
+ "option is specified but without a value, the value is implicitly "
+ "'true'.">;
}
let Command = "target modules dump" in {
def target_modules_dump_verbose : Option<"verbose", "v">,
- Desc<"Enable verbose dump.">;
+ Desc<"Enable verbose dump.">;
}
let Command = "target modules list" in {
- def target_modules_list_address : Option<"address", "a">, Group<1>,
- Arg<"AddressOrExpression">, Desc<"Display the image at this address.">;
- def target_modules_list_arch : Option<"arch", "A">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the architecture when listing images.">;
- def target_modules_list_triple : Option<"triple", "t">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the triple when listing images.">;
- def target_modules_list_header : Option<"header", "h">, Group<1>,
- Desc<"Display the image base address as a load address if debugging, a file"
- " address otherwise.">;
- def target_modules_list_offset : Option<"offset", "o">, Group<1>,
- Desc<"Display the image load address offset from the base file address "
- "(the slide amount).">;
- def target_modules_list_uuid : Option<"uuid", "u">, Group<1>,
- Desc<"Display the UUID when listing images.">;
- def target_modules_list_fullpath : Option<"fullpath", "f">, Group<1>,
- OptionalArg<"Width">,
- Desc<"Display the fullpath to the image object file.">;
- def target_modules_list_directory : Option<"directory", "d">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the directory with optional width for "
- "the image object file.">;
- def target_modules_list_basename : Option<"basename", "b">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the basename with optional width for "
- "the image object file.">;
- def target_modules_list_symfile : Option<"symfile", "s">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the fullpath to the image symbol file "
- "with optional width.">;
- def target_modules_list_symfile_unique : Option<"symfile-unique", "S">,
- Group<1>, OptionalArg<"Width">, Desc<"Display the symbol file with optional"
- " width only if it is different from the executable object file.">;
- def target_modules_list_mod_time : Option<"mod-time", "m">, Group<1>,
- OptionalArg<"Width">, Desc<"Display the modification time with optional "
- "width of the module.">;
- def target_modules_list_ref_count : Option<"ref-count", "r">, Group<1>,
- OptionalArg<"Width">, Desc<"Display whether the module is still in the "
- "the shared module cache (Y/N), and its shared pointer use_count.">;
- def target_modules_list_pointer : Option<"pointer", "p">, Group<1>,
- OptionalArg<"None">, Desc<"Display the module pointer.">;
- def target_modules_list_global : Option<"global", "g">, Group<1>,
- Desc<"Display the modules from the global module list, not just the "
- "current target.">;
+ def target_modules_list_address : Option<"address", "a">,
+ Group<1>,
+ Arg<"AddressOrExpression">,
+ Desc<"Display the image at this address.">;
+ def target_modules_list_arch
+ : Option<"arch", "A">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the architecture when listing images.">;
+ def target_modules_list_triple
+ : Option<"triple", "t">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the triple when listing images.">;
+ def target_modules_list_header : Option<"header", "h">,
+ Group<1>,
+ Desc<"Display the image base address as a "
+ "load address if debugging, a file"
+ " address otherwise.">;
+ def target_modules_list_offset
+ : Option<"offset", "o">,
+ Group<1>,
+ Desc<"Display the image load address offset from the base file address "
+ "(the slide amount).">;
+ def target_modules_list_uuid : Option<"uuid", "u">,
+ Group<1>,
+ Desc<"Display the UUID when listing images.">;
+ def target_modules_list_fullpath
+ : Option<"fullpath", "f">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the fullpath to the image object file.">;
+ def target_modules_list_directory
+ : Option<"directory", "d">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the directory with optional width for "
+ "the image object file.">;
+ def target_modules_list_basename
+ : Option<"basename", "b">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the basename with optional width for "
+ "the image object file.">;
+ def target_modules_list_symfile
+ : Option<"symfile", "s">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the fullpath to the image symbol file "
+ "with optional width.">;
+ def target_modules_list_symfile_unique
+ : Option<"symfile-unique", "S">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the symbol file with optional"
+ " width only if it is different from the executable object file.">;
+ def target_modules_list_mod_time
+ : Option<"mod-time", "m">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<"Display the modification time with optional "
+ "width of the module.">;
+ def target_modules_list_ref_count
+ : Option<"ref-count", "r">,
+ Group<1>,
+ OptionalArg<"Width">,
+ Desc<
+ "Display whether the module is still in the "
+ "the shared module cache (Y/N), and its shared pointer use_count.">;
+ def target_modules_list_pointer : Option<"pointer", "p">,
+ Group<1>,
+ OptionalArg<"None">,
+ Desc<"Display the module pointer.">;
+ def target_modules_list_global
+ : Option<"global", "g">,
+ Group<1>,
+ Desc<"Display the modules from the global module list, not just the "
+ "current target.">;
}
let Command = "target modules show unwind" in {
- def target_modules_show_unwind_name : Option<"name", "n">, Group<1>,
- Arg<"FunctionName">,
- Desc<"Show unwind instructions for a function or symbol name.">;
- def target_modules_show_unwind_address : Option<"address", "a">, Group<2>,
- Arg<"AddressOrExpression">, Desc<"Show unwind instructions for a function "
- "or symbol containing an address">;
- def target_modules_show_unwind_cached : Option<"cached", "c">,
- Arg<"Boolean">, Desc<"Show cached unwind information">;
+ def target_modules_show_unwind_name
+ : Option<"name", "n">,
+ Group<1>,
+ Arg<"FunctionName">,
+ Desc<"Show unwind instructions for a function or symbol name.">;
+ def target_modules_show_unwind_address
+ : Option<"address", "a">,
+ Group<2>,
+ Arg<"AddressOrExpression">,
+ Desc<"Show unwind instructions for a function "
+ "or symbol containing an address">;
+ def target_modules_show_unwind_cached
+ : Option<"cached", "c">,
+ Arg<"Boolean">,
+ Desc<"Show cached unwind information">;
}
let Command = "target modules lookup" in {
- def target_modules_lookup_address : Option<"address", "a">, Group<1>,
- Arg<"AddressOrExpression">, Required, Desc<"Lookup an address in one or "
- "more target modules.">;
- def target_modules_lookup_offset : Option<"offset", "o">, Group<1>,
- Arg<"Offset">, Desc<"When looking up an address subtract <offset> from any "
- "addresses before doing the lookup.">;
+ def target_modules_lookup_address : Option<"address", "a">,
+ Group<1>,
+ Arg<"AddressOrExpression">,
+ Required,
+ Desc<"Lookup an address in one or "
+ "more target modules.">;
+ def target_modules_lookup_offset
+ : Option<"offset", "o">,
+ Group<1>,
+ Arg<"Offset">,
+ Desc<"When looking up an address subtract <offset> from any "
+ "addresses before doing the lookup.">;
// FIXME: re-enable regex for types when the LookupTypeInModule actually uses
// the regex option by adding to group 6.
- def target_modules_lookup_regex : Option<"regex", "r">, Groups<[2,4,5]>,
- Desc<"The <name> argument for name lookups are regular expressions.">;
- def target_modules_lookup_symbol : Option<"symbol", "s">, Group<2>,
- Arg<"Symbol">, Required, Desc<"Lookup a symbol by name in the symbol tables"
- " in one or more target modules.">;
- def target_modules_lookup_file : Option<"file", "f">, Group<3>,
- Arg<"Filename">, Required, Desc<"Lookup a file by fullpath or basename in "
- "one or more target modules.">;
- def target_modules_lookup_line : Option<"line", "l">, Group<3>,
- Arg<"LineNum">, Desc<"Lookup a line number in a file (must be used in "
- "conjunction with --file).">;
- def target_modules_lookup_no_inlines : Option<"no-inlines", "i">,
- GroupRange<3,5>,
- Desc<"Ignore inline entries (must be used in conjunction with --file or "
- "--function).">;
- def target_modules_lookup_function : Option<"function", "F">, Group<4>,
- Arg<"FunctionName">, Required, Desc<"Lookup a function by name in the debug"
- " symbols in one or more target modules.">;
- def target_modules_lookup_name : Option<"name", "n">, Group<5>,
- Arg<"FunctionOrSymbol">, Required, Desc<"Lookup a function or symbol by "
- "name in one or more target modules.">;
- def target_modules_lookup_type : Option<"type", "t">, Group<6>, Arg<"Name">,
- Required, Desc<"Lookup a type by name in the debug symbols in one or more "
- "target modules.">;
- def target_modules_lookup_variables_ranges : Option<"show-variable-ranges",
- "\\x01">, GroupRange<1, 6>, Desc<"Dump valid ranges of variables (must be "
- "used in conjunction with --verbose">;
- def target_modules_lookup_verbose : Option<"verbose", "v">,
- Desc<"Enable verbose lookup information.">;
- def target_modules_lookup_all : Option<"all", "A">, Desc<"Print all matches, "
- "not just the best match, if a best match is available.">;
+ def target_modules_lookup_regex
+ : Option<"regex", "r">,
+ Groups<[2, 4, 5]>,
+ Desc<"The <name> argument for name lookups are regular expressions.">;
+ def target_modules_lookup_symbol
+ : Option<"symbol", "s">,
+ Group<2>,
+ Arg<"Symbol">,
+ Required,
+ Desc<"Lookup a symbol by name in the symbol tables"
+ " in one or more target modules.">;
+ def target_modules_lookup_file
+ : Option<"file", "f">,
+ Group<3>,
+ Arg<"Filename">,
+ Required,
+ Desc<"Lookup a file by fullpath or basename in "
+ "one or more target modules.">;
+ def target_modules_lookup_line
+ : Option<"line", "l">,
+ Group<3>,
+ Arg<"LineNum">,
+ Desc<"Lookup a line number in a file (must be used in "
+ "conjunction with --file).">;
+ def target_modules_lookup_no_inlines
+ : Option<"no-inlines", "i">,
+ GroupRange<3, 5>,
+ Desc<
+ "Ignore inline entries (must be used in conjunction with --file or "
+ "--function).">;
+ def target_modules_lookup_function
+ : Option<"function", "F">,
+ Group<4>,
+ Arg<"FunctionName">,
+ Required,
+ Desc<"Lookup a function by name in the debug"
+ " symbols in one or more target modules.">;
+ def target_modules_lookup_name : Option<"name", "n">,
+ Group<5>,
+ Arg<"FunctionOrSymbol">,
+ Required,
+ Desc<"Lookup a function or symbol by "
+ "name in one or more target modules.">;
+ def target_modules_lookup_type
+ : Option<"type", "t">,
+ Group<6>,
+ Arg<"Name">,
+ Required,
+ Desc<"Lookup a type by name in the debug symbols in one or more "
+ "target modules.">;
+ def target_modules_lookup_variables_ranges
+ : Option<"show-variable-ranges", "\\x01">,
+ GroupRange<1, 6>,
+ Desc<"Dump valid ranges of variables (must be used in conjunction with "
+ "--verbose">;
+ def target_modules_lookup_verbose
+ : Option<"verbose", "v">,
+ Desc<"Enable verbose lookup information.">;
+ def target_modules_lookup_all : Option<"all", "A">,
+ Desc<"Print all matches, not just the best "
+ "match, if a best match is available.">;
}
let Command = "target stop hook add" in {
- def target_stop_hook_add_one_liner : Option<"one-liner", "o">, GroupRange<1,3>,
- Arg<"OneLiner">, Desc<"Add a command for the stop hook. Can be specified "
- "more than once, and commands will be run in the order they appear.">;
- def target_stop_hook_add_shlib : Option<"shlib", "s">, Arg<"ShlibName">,
- Completion<"Module">,
- Desc<"Set the module within which the stop-hook is to be run.">;
- def target_stop_hook_add_thread_index : Option<"thread-index", "x">,
- Arg<"ThreadIndex">, Desc<"The stop hook is run only for the thread whose "
- "index matches this argument.">;
- def target_stop_hook_add_thread_id : Option<"thread-id", "t">,
- Arg<"ThreadID">, Desc<"The stop hook is run only for the thread whose TID "
- "matches this argument.">;
- def target_stop_hook_add_thread_name : Option<"thread-name", "T">,
- Arg<"ThreadName">, Desc<"The stop hook is run only for the thread whose "
- "thread name matches this argument.">;
- def target_stop_hook_add_queue_name : Option<"queue-name", "q">,
- Arg<"QueueName">, Desc<"The stop hook is run only for threads in the queue "
- "whose name is given by this argument.">;
- def target_stop_hook_add_file : Option<"file", "f">, Groups<[1,4]>,
- Arg<"Filename">, Desc<"Specify the source file within which the stop-hook "
- "is to be run.">, Completion<"SourceFile">;
- def target_stop_hook_add_start_line : Option<"start-line", "l">, Groups<[1,4]>,
- Arg<"LineNum">, Desc<"Set the start of the line range for which the "
- "stop-hook is to be run.">;
- def target_stop_hook_add_end_line : Option<"end-line", "e">, Groups<[1,4]>,
- Arg<"LineNum">, Desc<"Set the end of the line range for which the stop-hook"
- " is to be run.">;
- def target_stop_hook_add_classname : Option<"classname", "c">, Groups<[2,5]>,
- Arg<"ClassName">,
- Desc<"Specify the class within which the stop-hook is to be run.">;
- def target_stop_hook_add_name : Option<"name", "n">, Groups<[3,6]>,
- Arg<"FunctionName">, Desc<"Set the function name within which the stop hook"
- " will be run.">, Completion<"Symbol">;
- def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">,
- Arg<"Boolean">, Desc<"The stop-hook will auto-continue after running its"
- " commands.">;
- def target_stop_hook_add_at_initial_stop : Option<"at-initial-stop", "I">,
- Arg<"Boolean">, Desc<"Whether the stop-hook will trigger when lldb "
- "initially gains control of the process. For a process launch, this "
- "initial stop may happen very early on - before the loader has run. You "
- "can use this option if you do not want some stop-hooks to run then. "
- "Defaults to true.">;
+ def target_stop_hook_add_one_liner
+ : Option<"one-liner", "o">,
+ GroupRange<1, 3>,
+ Arg<"OneLiner">,
+ Desc<"Add a command for the stop hook. Can be specified more than "
+ "once, and commands will be run in the order they appear.">;
+ def target_stop_hook_add_shlib
+ : Option<"shlib", "s">,
+ Arg<"ShlibName">,
+ Completion<"Module">,
+ Desc<"Set the module within which the stop-hook is to be run.">;
+ def target_stop_hook_add_thread_index
+ : Option<"thread-index", "x">,
+ Arg<"ThreadIndex">,
+ Desc<"The stop hook is run only for the thread whose index matches "
+ "this argument.">;
+ def target_stop_hook_add_thread_id
+ : Option<"thread-id", "t">,
+ Arg<"ThreadID">,
+ Desc<"The stop hook is run only for the thread whose TID matches this "
+ "argument.">;
+ def target_stop_hook_add_thread_name
+ : Option<"thread-name", "T">,
+ Arg<"ThreadName">,
+ Desc<"The stop hook is run only for the thread whose thread name "
+ "matches this argument.">;
+ def target_stop_hook_add_queue_name
+ : Option<"queue-name", "q">,
+ Arg<"QueueName">,
+ Desc<"The stop hook is run only for threads in the ${q}ueue whose name "
+ "is given by this argument.">;
+ def target_stop_hook_add_file : Option<"file", "f">,
+ Groups<[1, 4]>,
+ Arg<"Filename">,
+ Desc<"Specify the source ${f}ile within "
+ "which the stop-hook is to be run.">,
+ Completion<"SourceFile">;
+ def target_stop_hook_add_start_line
+ : Option<"start-line", "l">,
+ Groups<[1, 4]>,
+ Arg<"LineNum">,
+ Desc<"Set the start of the ${l}ine range for which the "
+ "stop-hook is to be run.">;
+ def target_stop_hook_add_end_line
+ : Option<"end-line", "e">,
+ Groups<[1, 4]>,
+ Arg<"LineNum">,
+ Desc<"Set the ${e}nd of the line range for which the stop-hook is to "
+ "be run.">;
+ def target_stop_hook_add_classname
+ : Option<"classname", "c">,
+ Groups<[2, 5]>,
+ Arg<"ClassName">,
+ Desc<"Specify the ${c}lass within which the stop-hook is to be run.">;
+ def target_stop_hook_add_name
+ : Option<"name", "n">,
+ Groups<[3, 6]>,
+ Arg<"FunctionName">,
+ Desc<
+ "Set the ${f}unction name within which the stop hook will be run.">,
+ Completion<"Symbol">;
+ def target_stop_hook_add_auto_continue
+ : Option<"auto-continue", "G">,
+ Arg<"Boolean">,
+ Desc<"The stop-hook will auto-continue after running its commands.">;
+ def target_stop_hook_add_at_initial_stop
+ : Option<"at-initial-stop", "I">,
+ Arg<"Boolean">,
+ Desc<"Whether the stop-hook will trigger when lldb ${i}nitially gains "
+ "control of the process. For a process launch, this initial stop "
+ "may happen very early on - before the loader has run. You can "
+ "use this option if you do not want some stop-hooks to run then. "
+ "Defaults to true.">;
}
let Command = "thread backtrace" in {
- def thread_backtrace_count : Option<"count", "c">, Group<1>, Arg<"Count">,
- Desc<"How many frames to display (0 for all)">;
- def thread_backtrace_start : Option<"start", "s">, Group<1>,
- Arg<"FrameIndex">, Desc<"Frame in which to start the backtrace">;
- def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
- Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
- def thread_backtrace_unfiltered : Option<"unfiltered", "u">, Group<1>,
- Desc<"Do not filter out frames according to installed frame recognizers">;
+ def thread_backtrace_count : Option<"count", "c">,
+ Group<1>,
+ Arg<"Count">,
+ Desc<"How many frames to display (0 for all)">;
+ def thread_backtrace_start : Option<"start", "s">,
+ Group<1>,
+ Arg<"FrameIndex">,
+ Desc<"Frame in which to ${s}tart the backtrace">;
+ def thread_backtrace_extended
+ : Option<"extended", "e">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Show the ${e}xtended backtrace, if available">;
+ def thread_backtrace_unfiltered : Option<"unfiltered", "u">,
+ Group<1>,
+ Desc<"Do not filter out frames according "
+ "to installed frame recognizers">;
}
let Command = "thread step scope" in {
- def thread_step_scope_step_in_avoids_no_debug :
- Option<"step-in-avoids-no-debug", "a">, Group<1>, Arg<"Boolean">,
- Desc<"A boolean value that sets whether stepping into functions will step "
- "over functions with no debug information.">;
- def thread_step_scope_step_out_avoids_no_debug :
- Option<"step-out-avoids-no-debug", "A">, Group<1>, Arg<"Boolean">,
- Desc<"A boolean value, if true stepping out of functions will continue to"
- " step out till it hits a function with debug information.">;
- def thread_step_scope_count : Option<"count", "c">, Group<1>, Arg<"Count">,
- Desc<"How many times to perform the stepping operation - currently only "
- "supported for step-inst and next-inst.">;
- def thread_step_scope_end_linenumber : Option<"end-linenumber", "e">,
- Group<1>, Arg<"LineNum">, Desc<"The line at which to stop stepping - "
- "defaults to the next line and only supported for step-in and step-over."
- " You can also pass the string 'block' to step to the end of the current"
- " block. This is particularly use in conjunction with --step-target to"
- " step through a complex calling sequence.">;
- def thread_step_scope_run_mode : Option<"run-mode", "m">, Group<1>,
- EnumArg<"RunMode">, Desc<"Determine how to run other "
- "threads while stepping the current thread.">;
- def thread_step_scope_step_over_regexp : Option<"step-over-regexp", "r">,
- Group<1>, Arg<"RegularExpression">, Desc<"A regular expression that defines "
- "function names to not to stop at when stepping in.">;
- def thread_step_scope_step_in_target : Option<"step-in-target", "t">,
- Group<1>, Arg<"FunctionName">, Desc<"The name of the directly called "
- "function step in should stop at when stepping into.">;
+ def thread_step_scope_step_in_avoids_no_debug
+ : Option<"step-in-avoids-no-debug", "a">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"A boolean value that sets whether stepping into functions will "
+ "step over functions with no debug information.">;
+ def thread_step_scope_step_out_avoids_no_debug
+ : Option<"step-out-avoids-no-debug", "A">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"A boolean value, if true stepping out of functions will continue "
+ "to step out till it hits a function with debug information.">;
+ def thread_step_scope_count
+ : Option<"count", "c">,
+ Group<1>,
+ Arg<"Count">,
+ Desc<"How many times to perform the stepping operation - currently "
+ "only supported for step-inst and next-inst.">;
+ def thread_step_scope_end_linenumber
+ : Option<"end-linenumber", "e">,
+ Group<1>,
+ Arg<"LineNum">,
+ Desc<"The line at which to stop stepping - defaults to the next line "
+ "and only supported for step-in and step-over. You can also pass "
+ "the string 'block' to step to the end of the current block. This "
+ "is particularly useful in conjunction with --step-target to step "
+ "through a complex calling sequence.">;
+ def thread_step_scope_run_mode : Option<"run-mode", "m">,
+ Group<1>,
+ EnumArg<"RunMode">,
+ Desc<"Determine how to run other threads "
+ "while stepping the current thread.">;
+ def thread_step_scope_step_over_regexp
+ : Option<"step-over-regexp", "r">,
+ Group<1>,
+ Arg<"RegularExpression">,
+ Desc<"A ${r}egular expression that defines function names to not to "
+ "stop at when stepping in.">;
+ def thread_step_scope_step_in_target
+ : Option<"step-in-target", "t">,
+ Group<1>,
+ Arg<"FunctionName">,
+ Desc<"The name of the directly called function step in should stop at "
+ "when stepping into.">;
}
let Command = "thread until" in {
- def thread_until_frame : Option<"frame", "f">, Group<1>, Arg<"FrameIndex">,
- Desc<"Frame index for until operation - defaults to 0">;
- def thread_until_thread : Option<"thread", "t">, Group<1>, Arg<"ThreadIndex">,
- Desc<"Thread index for the thread for until operation">;
- def thread_until_run_mode : Option<"run-mode", "m">, Group<1>,
- EnumArg<"RunMode">, Desc<"Determine how to run other "
- "threads while stepping this one">;
- def thread_until_address : Option<"address", "a">, Group<1>,
- Arg<"AddressOrExpression">, Desc<"Run until we reach the specified address, "
- "or leave the function - can be specified multiple times.">;
+ def thread_until_frame
+ : Option<"frame", "f">,
+ Group<1>,
+ Arg<"FrameIndex">,
+ Desc<"Frame index for until operation - defaults to 0">;
+ def thread_until_thread
+ : Option<"thread", "t">,
+ Group<1>,
+ Arg<"ThreadIndex">,
+ Desc<"Thread index for the thread for until operation">;
+ def thread_until_run_mode : Option<"run-mode", "m">,
+ Group<1>,
+ EnumArg<"RunMode">,
+ Desc<"Determine how to run other "
+ "threads while stepping this one">;
+ def thread_until_address
+ : Option<"address", "a">,
+ Group<1>,
+ Arg<"AddressOrExpression">,
+ Desc<"Run until we reach the specified address, "
+ "or leave the function - can be specified multiple times.">;
}
let Command = "thread info" in {
- def thread_info_json : Option<"json", "j">, Desc<"Display the thread info in"
- " JSON format.">;
- def thread_info_stop_info : Option<"stop-info", "s">, Desc<"Display the "
- "extended stop info in JSON format.">;
+ def thread_info_json : Option<"json", "j">,
+ Desc<"Display the thread info in"
+ " JSON format.">;
+ def thread_info_stop_info : Option<"stop-info", "s">,
+ Desc<"Display the "
+ "extended stop info in JSON format.">;
def thread_info_backing_thread : Option<"backing-thread", "b">,
- Desc<"If this is an OS plugin thread, query the backing thread instead; has"
- " no effect otherwise.">;
+ Desc<"If this is an OS plugin thread, query "
+ "the backing thread instead; has"
+ " no effect otherwise.">;
}
let Command = "thread return" in {
- def thread_return_from_expression : Option<"from-expression", "x">,
- Desc<"Return from the innermost expression evaluation.">;
+ def thread_return_from_expression
+ : Option<"from-expression", "x">,
+ Desc<"Return from the innermost expression evaluation.">;
}
let Command = "thread jump" in {
- def thread_jump_file : Option<"file", "f">, Group<1>, Arg<"Filename">,
- Completion<"SourceFile">, Desc<"Specifies the source file to jump to.">;
- def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
- Required, Desc<"Specifies the line number to jump to.">;
- def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required,
- Desc<"Jumps by a relative line offset from the current line,"
- "can be a positive or negative offset">;
- def thread_jump_address : Option<"address", "a">, Group<3>,
- Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">;
- def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>,
- Desc<"Allows the PC to leave the current function.">;
+ def thread_jump_file : Option<"file", "f">,
+ Group<1>,
+ Arg<"Filename">,
+ Completion<"SourceFile">,
+ Desc<"Specifies the source file to jump to.">;
+ def thread_jump_line : Option<"line", "l">,
+ Group<1>,
+ Arg<"LineNum">,
+ Required,
+ Desc<"Specifies the line number to jump to.">;
+ def thread_jump_by
+ : Option<"by", "b">,
+ Group<2>,
+ Arg<"Offset">,
+ Required,
+ Desc<"Jumps by a relative line offset from the current line, "
+ "can be a positive or negative offset.">;
+ def thread_jump_address : Option<"address", "a">,
+ Group<3>,
+ Arg<"AddressOrExpression">,
+ Required,
+ Desc<"Jumps to a specific address.">;
+ def thread_jump_force : Option<"force", "r">,
+ Groups<[1, 2, 3]>,
+ Desc<"Allows the PC to leave the current function.">;
}
let Command = "thread plan list" in {
- def thread_plan_list_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Display more information about the thread plans">;
- def thread_plan_list_internal : Option<"internal", "i">, Group<1>,
- Desc<"Display internal as well as user thread plans">;
- def thread_plan_list_thread_id : Option<"thread-id", "t">, Group<1>,
- Arg<"ThreadID">, Desc<"List the thread plans for this TID, can be "
- "specified more than once.">;
- def thread_plan_list_unreported : Option<"unreported", "u">, Group<1>,
- Desc<"Display thread plans for unreported threads">;
+ def thread_plan_list_verbose
+ : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Display more information about the thread plans">;
+ def thread_plan_list_internal
+ : Option<"internal", "i">,
+ Group<1>,
+ Desc<"Display internal as well as user thread plans">;
+ def thread_plan_list_thread_id
+ : Option<"thread-id", "t">,
+ Group<1>,
+ Arg<"ThreadID">,
+ Desc<"List the thread plans for this TID, can be "
+ "specified more than once.">;
+ def thread_plan_list_unreported
+ : Option<"unreported", "u">,
+ Group<1>,
+ Desc<"Display thread plans for unreported threads">;
}
let Command = "thread select" in {
- def thread_select_thread_id : Option<"thread-id", "t">, Group<2>,
- Arg<"ThreadID">, Completion<"ThreadID">,
- Desc<"Provide a thread ID instead of a thread index.">;
+ def thread_select_thread_id
+ : Option<"thread-id", "t">,
+ Group<2>,
+ Arg<"ThreadID">,
+ Completion<"ThreadID">,
+ Desc<"Provide a thread ID instead of a thread index.">;
}
let Command = "thread trace dump function calls" in {
- def thread_trace_dump_function_calls_file : Option<"file", "F">, Group<1>,
- Arg<"Filename">,
- Desc<"Dump the function calls to a file instead of the standard output.">;
- def thread_trace_dump_function_calls_json: Option<"json", "j">,
- Group<1>,
- Desc<"Dump in simple JSON format.">;
- def thread_trace_dump_function_calls_pretty_json: Option<"pretty-json", "J">,
- Group<1>,
- Desc<"Dump in JSON format but pretty printing the output for easier "
- "readability.">;
+ def thread_trace_dump_function_calls_file
+ : Option<"file", "F">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Dump the function calls to a file instead of the standard "
+ "output.">;
+ def thread_trace_dump_function_calls_json
+ : Option<"json", "j">,
+ Group<1>,
+ Desc<"Dump in simple JSON format.">;
+ def thread_trace_dump_function_calls_pretty_json
+ : Option<"pretty-json", "J">,
+ Group<1>,
+ Desc<"Dump in JSON format but pretty printing the output for easier "
+ "readability.">;
}
let Command = "thread trace dump instructions" in {
- def thread_trace_dump_instructions_forwards: Option<"forwards", "f">,
- Group<1>,
- Desc<"If specified, the trace is traversed forwards chronologically "
- "starting at the oldest instruction. Otherwise, it starts at the most "
- "recent one and the traversal is backwards.">;
- def thread_trace_dump_instructions_count : Option<"count", "c">, Group<1>,
- Arg<"Count">,
- Desc<"The number of instructions to display starting at the most recent "
- "instruction, or the oldest if --forwards is provided.">;
- def thread_trace_dump_instructions_all : Option<"all", "a">, Group<1>,
- Desc<"From the starting point of the trace, dump all instructions "
- "available.">;
- def thread_trace_dump_instructions_id: Option<"id", "i">, Group<1>,
- Arg<"Index">,
- Desc<"Custom starting instruction id from where to start traversing. This "
- "id can be provided in decimal or hexadecimal representation.">;
- def thread_trace_dump_instructions_skip: Option<"skip", "s">, Group<1>,
- Arg<"Index">,
- Desc<"How many trace items (instructions, errors and events) to skip from "
- "the starting position of the trace before starting the traversal.">;
- def thread_trace_dump_instructions_raw : Option<"raw", "r">, Group<1>,
- Desc<"Dump only instruction address without disassembly nor symbol "
- "information.">;
- def thread_trace_dump_instructions_file : Option<"file", "F">, Group<1>,
- Arg<"Filename">,
- Desc<"Dump the instruction to a file instead of the standard output.">;
- def thread_trace_dump_instructions_json: Option<"json", "j">,
- Group<1>,
- Desc<"Dump in simple JSON format.">;
- def thread_trace_dump_instructions_pretty_print: Option<"pretty-json", "J">,
- Group<1>,
- Desc<"Dump in JSON format but pretty printing the output for easier "
- "readability.">;
- def thread_trace_dump_instructions_show_kind : Option<"kind", "k">, Group<1>,
- Desc<"Show instruction control flow kind. Refer to the enum "
- "`InstructionControlFlowKind` for a list of control flow kind. "
- "As an important note, far jumps, far calls and far returns often indicate "
- "calls to and from kernel.">;
- def thread_trace_dump_instructions_show_timestamps: Option<"time", "t">,
- Group<1>,
- Desc<"For each trace item, print the corresponding wall clock timestamp "
- "if available.">;
- def thread_trace_dump_instructions_show_events : Option<"events", "e">,
- Group<1>,
- Desc<"Dump the events that happened during the execution of the target.">;
- def thread_trace_dump_instruction_only_events : Option<"only-events", "E">,
- Group<1>,
- Desc<"Dump only the events that happened during the execution of the "
- "target. No instructions are dumped.">;
- def thread_trace_dump_instructions_continue: Option<"continue", "C">,
- Group<1>,
- Desc<"Continue dumping instructions right where the previous invocation of "
- "this command was left, or from the beginning if this is the first "
- "invocation. The --skip argument is discarded and the other arguments are "
- "preserved from the previous invocation when possible.">;
+ def thread_trace_dump_instructions_forwards
+ : Option<"forwards", "f">,
+ Group<1>,
+ Desc<"If specified, the trace is traversed forwards chronologically "
+ "starting at the oldest instruction. Otherwise, it starts at the "
+ "most "
+ "recent one and the traversal is backwards.">;
+ def thread_trace_dump_instructions_count
+ : Option<"count", "c">,
+ Group<1>,
+ Arg<"Count">,
+ Desc<
+ "The number of instructions to display starting at the most recent "
+ "instruction, or the oldest if --forwards is provided.">;
+ def thread_trace_dump_instructions_all
+ : Option<"all", "a">,
+ Group<1>,
+ Desc<"From the starting point of the trace, dump all instructions "
+ "available.">;
+ def thread_trace_dump_instructions_id
+ : Option<"id", "i">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"Custom starting instruction id from where to start traversing. "
+ "This "
+ "id can be provided in decimal or hexadecimal representation.">;
+ def thread_trace_dump_instructions_skip
+ : Option<"skip", "s">,
+ Group<1>,
+ Arg<"Index">,
+ Desc<"How many trace items (instructions, errors and events) to skip "
+ "from "
+ "the starting position of the trace before starting the "
+ "traversal.">;
+ def thread_trace_dump_instructions_raw
+ : Option<"raw", "r">,
+ Group<1>,
+ Desc<"Dump only instruction address without disassembly nor symbol "
+ "information.">;
+ def thread_trace_dump_instructions_file
+ : Option<"file", "F">,
+ Group<1>,
+ Arg<"Filename">,
+ Desc<"Dump the instruction to a file instead of the standard output.">;
+ def thread_trace_dump_instructions_json : Option<"json", "j">,
+ Group<1>,
+ Desc<"Dump in simple JSON format.">;
+ def thread_trace_dump_instructions_pretty_print
+ : Option<"pretty-json", "J">,
+ Group<1>,
+ Desc<"Dump in JSON format but pretty printing the output for easier "
+ "readability.">;
+ def thread_trace_dump_instructions_show_kind
+ : Option<"kind", "k">,
+ Group<1>,
+ Desc<"Show instruction control flow kind. Refer to the enum "
+ "`InstructionControlFlowKind` for a list of control flow kind. "
+ "As an important note, far jumps, far calls and far returns often "
+ "indicate "
+ "calls to and from kernel.">;
+ def thread_trace_dump_instructions_show_timestamps
+ : Option<"time", "t">,
+ Group<1>,
+ Desc<
+ "For each trace item, print the corresponding wall clock timestamp "
+ "if available.">;
+ def thread_trace_dump_instructions_show_events
+ : Option<"events", "e">,
+ Group<1>,
+ Desc<"Dump the events that happened during the execution of the "
+ "target.">;
+ def thread_trace_dump_instruction_only_events
+ : Option<"only-events", "E">,
+ Group<1>,
+ Desc<"Dump only the events that happened during the execution of the "
+ "target. No instructions are dumped.">;
+ def thread_trace_dump_instructions_continue
+ : Option<"continue", "C">,
+ Group<1>,
+ Desc<
+ "Continue dumping instructions right where the previous invocation "
+ "of "
+ "this command was left, or from the beginning if this is the first "
+ "invocation. The --skip argument is discarded and the other "
+ "arguments are "
+ "preserved from the previous invocation when possible.">;
}
let Command = "thread trace dump info" in {
- def thread_trace_dump_info_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"show verbose thread trace dump info">;
- def thread_trace_dump_info_json: Option<"json", "j">, Group<1>,
- Desc<"Dump in JSON format.">;
+ def thread_trace_dump_info_verbose
+ : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"show verbose thread trace dump info">;
+ def thread_trace_dump_info_json : Option<"json", "j">,
+ Group<1>,
+ Desc<"Dump in JSON format.">;
}
let Command = "type summary add" in {
- def type_summary_add_category : Option<"category", "w">, Arg<"Name">,
- Desc<"Add this to the given category instead of the default one.">;
- def type_summary_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
- Desc<"If true, cascade through typedef chains.">;
- def type_summary_add_no_value : Option<"no-value", "v">,
- Desc<"Don't show the value, just show the summary, for this type.">;
- def type_summary_add_skip_pointers : Option<"skip-pointers", "p">,
- Desc<"Don't use this format for pointers-to-type objects.">;
- def type_summary_add_pointer_match_depth : Option<"pointer-match-depth", "d">,
- Arg<"UnsignedInteger">,
- Desc<"Specify the maximum pointer depth that this format can be apply to "
- "(default to 1). It's only effective when --skip-pointers is not set.">;
- def type_summary_add_skip_references : Option<"skip-references", "r">,
- Desc<"Don't use this format for references-to-type objects.">;
- def type_summary_add_regex : Option<"regex", "x">,
- Desc<"Type names are actually regular expressions.">;
- def type_summary_add_recognizer_function :
- Option<"recognizer-function", "\\x01">,
- Desc<"The names in the argument list are actually the names of python "
- "functions that decide whether to use this summary for any given type. "
- "Cannot be specified at the same time as --regex (-x).">;
- def type_summary_add_inline_children : Option<"inline-children", "c">,
- Group<1>, Required,
- Desc<"If true, inline all child values into summary string.">;
- def type_summary_add_omit_names : Option<"omit-names", "O">, Group<1>,
- Desc<"If true, omit value names in the summary display.">;
- def type_summary_add_summary_string : Option<"summary-string", "s">, Group<2>,
- Arg<"SummaryString">, Required,
- Desc<"Summary string used to display text and object contents.">;
- def type_summary_add_python_script : Option<"python-script", "o">, Group<3>,
- Arg<"PythonScript">,
- Desc<"Give a one-liner Python script as part of the command.">;
- def type_summary_add_python_function : Option<"python-function", "F">,
- Group<3>, Arg<"PythonFunction">,
- Desc<"Give the name of a Python function to use for this type.">;
- def type_summary_add_input_python : Option<"input-python", "P">, Group<3>,
- Desc<"Input Python code to use for this type manually.">;
- def type_summary_add_expand : Option<"expand", "e">, Groups<[2,3]>,
- Desc<"Expand aggregate data types to show children on separate lines.">;
- def type_summary_add_hide_empty : Option<"hide-empty", "h">, Groups<[2,3]>,
- Desc<"Do not expand aggregate data types with no children.">;
- def type_summary_add_name : Option<"name", "n">, Groups<[2,3]>, Arg<"Name">,
- Desc<"A name for this summary string.">;
+ def type_summary_add_category
+ : Option<"category", "w">,
+ Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_summary_add_cascade
+ : Option<"cascade", "C">,
+ Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_summary_add_no_value
+ : Option<"no-value", "v">,
+ Desc<"Don't show the value, just show the summary, for this type.">;
+ def type_summary_add_skip_pointers
+ : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_summary_add_pointer_match_depth
+ : Option<"pointer-match-depth", "d">,
+ Arg<"UnsignedInteger">,
+ Desc<"Specify the maximum pointer depth that this format can be apply "
+ "to "
+ "(default to 1). It's only effective when --skip-pointers is not "
+ "set.">;
+ def type_summary_add_skip_references
+ : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_summary_add_regex
+ : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_summary_add_recognizer_function
+ : Option<"recognizer-function", "\\x01">,
+ Desc<"The names in the argument list are actually the names of python "
+ "functions that decide whether to use this summary for any given "
+ "type. "
+ "Cannot be specified at the same time as --regex (-x).">;
+ def type_summary_add_inline_children
+ : Option<"inline-children", "c">,
+ Group<1>,
+ Required,
+ Desc<"If true, inline all child values into summary string.">;
+ def type_summary_add_omit_names
+ : Option<"omit-names", "O">,
+ Group<1>,
+ Desc<"If true, omit value names in the summary display.">;
+ def type_summary_add_summary_string
+ : Option<"summary-string", "s">,
+ Group<2>,
+ Arg<"SummaryString">,
+ Required,
+ Desc<"Summary string used to display text and object contents.">;
+ def type_summary_add_python_script
+ : Option<"python-script", "o">,
+ Group<3>,
+ Arg<"PythonScript">,
+ Desc<"Give a one-liner Python script as part of the command.">;
+ def type_summary_add_python_function
+ : Option<"python-function", "F">,
+ Group<3>,
+ Arg<"PythonFunction">,
+ Desc<"Give the name of a Python function to use for this type.">;
+ def type_summary_add_input_python
+ : Option<"input-python", "P">,
+ Group<3>,
+ Desc<"Input Python code to use for this type manually.">;
+ def type_summary_add_expand
+ : Option<"expand", "e">,
+ Groups<[2, 3]>,
+ Desc<"Expand aggregate data types to show children on separate lines.">;
+ def type_summary_add_hide_empty
+ : Option<"hide-empty", "h">,
+ Groups<[2, 3]>,
+ Desc<"Do not expand aggregate data types with no children.">;
+ def type_summary_add_name : Option<"name", "n">,
+ Groups<[2, 3]>,
+ Arg<"Name">,
+ Desc<"A name for this summary string.">;
}
let Command = "type synth add" in {
- def type_synth_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
- Desc<"If true, cascade through typedef chains.">;
- def type_synth_add_skip_pointers : Option<"skip-pointers", "p">,
- Desc<"Don't use this format for pointers-to-type objects.">;
- def type_synth_add_skip_references : Option<"skip-references", "r">,
- Desc<"Don't use this format for references-to-type objects.">;
- def type_synth_add_category : Option<"category", "w">, Arg<"Name">,
- Desc<"Add this to the given category instead of the default one.">;
- def type_synth_add_python_class : Option<"python-class", "l">, Group<2>,
- Arg<"PythonClass">,
- Desc<"Use this Python class to produce synthetic children.">;
- def type_synth_add_input_python : Option<"input-python", "P">, Group<3>,
- Desc<"Type Python code to generate a class that provides synthetic "
- "children.">;
- def type_synth_add_regex : Option<"regex", "x">,
- Desc<"Type names are actually regular expressions.">;
- def type_synth_add_recognizer_function :
- Option<"recognizer-function", "\\x01">,
- Desc<"The names in the argument list are actually the names of python "
- "functions that decide whether to use this summary for any given type. "
- "Cannot be specified at the same time as --regex (-x).">;
+ def type_synth_add_cascade : Option<"cascade", "C">,
+ Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_synth_add_skip_pointers
+ : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_synth_add_skip_references
+ : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_synth_add_category
+ : Option<"category", "w">,
+ Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_synth_add_python_class
+ : Option<"python-class", "l">,
+ Group<2>,
+ Arg<"PythonClass">,
+ Desc<"Use this Python class to produce synthetic children.">;
+ def type_synth_add_input_python
+ : Option<"input-python", "P">,
+ Group<3>,
+ Desc<"Type Python code to generate a class that provides synthetic "
+ "children.">;
+ def type_synth_add_regex
+ : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_synth_add_recognizer_function
+ : Option<"recognizer-function", "\\x01">,
+ Desc<"The names in the argument list are actually the names of python "
+ "functions that decide whether to use this summary for any given "
+ "type. "
+ "Cannot be specified at the same time as --regex (-x).">;
}
let Command = "type format add" in {
- def type_format_add_category : Option<"category", "w">, Arg<"Name">,
- Desc<"Add this to the given category instead of the default one.">;
- def type_format_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
- Desc<"If true, cascade through typedef chains.">;
- def type_format_add_skip_pointers : Option<"skip-pointers", "p">,
- Desc<"Don't use this format for pointers-to-type objects.">;
- def type_format_add_skip_references : Option<"skip-references", "r">,
- Desc<"Don't use this format for references-to-type objects.">;
- def type_format_add_regex : Option<"regex", "x">,
- Desc<"Type names are actually regular expressions.">;
- def type_format_add_type : Option<"type", "t">, Group<2>, Arg<"Name">,
- Desc<"Format variables as if they were of this type.">;
+ def type_format_add_category
+ : Option<"category", "w">,
+ Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_format_add_cascade
+ : Option<"cascade", "C">,
+ Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_format_add_skip_pointers
+ : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_format_add_skip_references
+ : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_format_add_regex
+ : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
+ def type_format_add_type
+ : Option<"type", "t">,
+ Group<2>,
+ Arg<"Name">,
+ Desc<"Format variables as if they were of this type.">;
}
let Command = "type formatter delete" in {
- def type_formatter_delete_all : Option<"all", "a">, Group<1>,
- Desc<"Delete from every category.">;
- def type_formatter_delete_category : Option<"category", "w">, Group<2>,
- Arg<"Name">, Desc<"Delete from given category.">;
- def type_formatter_delete_language : Option<"language", "l">, Group<3>,
- Arg<"Language">, Desc<"Delete from given language's category.">;
+ def type_formatter_delete_all : Option<"all", "a">,
+ Group<1>,
+ Desc<"Delete from every category.">;
+ def type_formatter_delete_category : Option<"category", "w">,
+ Group<2>,
+ Arg<"Name">,
+ Desc<"Delete from given category.">;
+ def type_formatter_delete_language
+ : Option<"language", "l">,
+ Group<3>,
+ Arg<"Language">,
+ Desc<"Delete from given language's category.">;
}
let Command = "type formatter clear" in {
def type_formatter_clear_all : Option<"all", "a">,
- Desc<"Clear every category.">;
+ Desc<"Clear every category.">;
}
let Command = "type formatter list" in {
- def type_formatter_list_category_regex : Option<"category-regex", "w">,
- Group<1>, Arg<"Name">, Desc<"Only show categories matching this filter.">;
- def type_formatter_list_language : Option<"language", "l">, Group<2>,
- Arg<"Language">, Desc<"Only show the category for a specific language.">;
+ def type_formatter_list_category_regex
+ : Option<"category-regex", "w">,
+ Group<1>,
+ Arg<"Name">,
+ Desc<"Only show categories matching this filter.">;
+ def type_formatter_list_language
+ : Option<"language", "l">,
+ Group<2>,
+ Arg<"Language">,
+ Desc<"Only show the category for a specific language.">;
}
let Command = "type category define" in {
- def type_category_define_enabled : Option<"enabled", "e">,
- Desc<"If specified, this category will be created enabled.">;
- def type_category_define_language : Option<"language", "l">, Arg<"Language">,
- Desc<"Specify the language that this category is supported for.">;
+ def type_category_define_enabled
+ : Option<"enabled", "e">,
+ Desc<"If specified, this category will be created enabled.">;
+ def type_category_define_language
+ : Option<"language", "l">,
+ Arg<"Language">,
+ Desc<"Specify the language that this category is supported for.">;
}
let Command = "type category enable" in {
- def type_category_enable_language : Option<"language", "l">, Arg<"Language">,
- Desc<"Enable the category for this language.">;
+ def type_category_enable_language
+ : Option<"language", "l">,
+ Arg<"Language">,
+ Desc<"Enable the category for this language.">;
}
let Command = "type category disable" in {
- def type_category_disable_language : Option<"language", "l">, Arg<"Language">,
- Desc<"Enable the category for this language.">;
+ def type_category_disable_language
+ : Option<"language", "l">,
+ Arg<"Language">,
+ Desc<"Disable the category for this language.">;
}
let Command = "type filter add" in {
- def type_filter_add_cascade : Option<"cascade", "C">, Arg<"Boolean">,
- Desc<"If true, cascade through typedef chains.">;
- def type_filter_add_skip_pointers : Option<"skip-pointers", "p">,
- Desc<"Don't use this format for pointers-to-type objects.">;
- def type_filter_add_skip_references : Option<"skip-references", "r">,
- Desc<"Don't use this format for references-to-type objects.">;
- def type_filter_add_category : Option<"category", "w">, Arg<"Name">,
- Desc<"Add this to the given category instead of the default one.">;
- def type_filter_add_child : Option<"child", "c">, Arg<"ExpressionPath">,
- Desc<"Include this expression path in the synthetic view.">;
- def type_filter_add_regex : Option<"regex", "x">,
- Desc<"Type names are actually regular expressions.">;
+ def type_filter_add_cascade
+ : Option<"cascade", "C">,
+ Arg<"Boolean">,
+ Desc<"If true, cascade through typedef chains.">;
+ def type_filter_add_skip_pointers
+ : Option<"skip-pointers", "p">,
+ Desc<"Don't use this format for pointers-to-type objects.">;
+ def type_filter_add_skip_references
+ : Option<"skip-references", "r">,
+ Desc<"Don't use this format for references-to-type objects.">;
+ def type_filter_add_category
+ : Option<"category", "w">,
+ Arg<"Name">,
+ Desc<"Add this to the given category instead of the default one.">;
+ def type_filter_add_child
+ : Option<"child", "c">,
+ Arg<"ExpressionPath">,
+ Desc<"Include this expression path in the synthetic view.">;
+ def type_filter_add_regex
+ : Option<"regex", "x">,
+ Desc<"Type names are actually regular expressions.">;
}
let Command = "type lookup" in {
def type_lookup_show_help : Option<"show-help", "h">,
- Desc<"Display available help for types">;
- def type_lookup_language : Option<"language", "l">, Arg<"Language">,
- Desc<"Which language's types should the search scope be">;
+ Desc<"Display available help for types">;
+ def type_lookup_language
+ : Option<"language", "l">,
+ Arg<"Language">,
+ Desc<"Which language's types should the search scope be">;
}
let Command = "watchpoint list" in {
- def watchpoint_list_brief : Option<"brief", "b">, Group<1>, Desc<"Give a "
- "brief description of the watchpoint (no location info).">;
- def watchpoint_list_full : Option<"full", "f">, Group<2>, Desc<"Give a full "
- "description of the watchpoint and its locations.">;
- def watchpoint_list_verbose : Option<"verbose", "v">, Group<3>, Desc<"Explain "
- "everything we know about the watchpoint (for debugging debugger bugs).">;
+ def watchpoint_list_brief
+ : Option<"brief", "b">,
+ Group<1>,
+ Desc<"Give a "
+ "brief description of the watchpoint (no location info).">;
+ def watchpoint_list_full
+ : Option<"full", "f">,
+ Group<2>,
+ Desc<"Give a full "
+ "description of the watchpoint and its locations.">;
+ def watchpoint_list_verbose : Option<"verbose", "v">,
+ Group<3>,
+ Desc<"Explain "
+ "everything we know about the watchpoint "
+ "(for debugging debugger bugs).">;
}
let Command = "watchpoint ignore" in {
- def watchpoint_ignore_ignore_count : Option<"ignore-count", "i">,
- Arg<"Count">, Required, Desc<"Set the number of times this watchpoint is"
- " skipped before stopping.">;
+ def watchpoint_ignore_ignore_count
+ : Option<"ignore-count", "i">,
+ Arg<"Count">,
+ Required,
+ Desc<"Set the number of times this watchpoint is"
+ " skipped before stopping.">;
}
let Command = "watchpoint modify" in {
- def watchpoint_modify_condition : Option<"condition", "c">, Arg<"Expression">,
- Desc<"The watchpoint stops only if this condition expression evaluates "
- "to true.">;
+ def watchpoint_modify_condition
+ : Option<"condition", "c">,
+ Arg<"Expression">,
+ Desc<"The watchpoint stops only if this condition expression evaluates "
+ "to true.">;
}
let Command = "watchpoint command add" in {
- def watchpoint_command_add_one_liner : Option<"one-liner", "o">, Group<1>,
- Arg<"OneLiner">, Desc<"Specify a one-line watchpoint command inline. Be "
- "sure to surround it with quotes.">;
- def watchpoint_command_add_stop_on_error : Option<"stop-on-error", "e">,
- Arg<"Boolean">, Desc<"Specify whether watchpoint command execution should "
- "terminate on error.">;
- def watchpoint_command_add_script_type : Option<"script-type", "s">,
- EnumArg<"ScriptLang">, Desc<"Specify the language for the"
- " commands - if none is specified, the lldb command interpreter will be "
- "used.">;
- def watchpoint_command_add_python_function : Option<"python-function", "F">,
- Group<2>, Arg<"PythonFunction">, Desc<"Give the name of a Python function "
- "to run as command for this watchpoint. Be sure to give a module name if "
- "appropriate.">;
+ def watchpoint_command_add_one_liner
+ : Option<"one-liner", "o">,
+ Group<1>,
+ Arg<"OneLiner">,
+ Desc<"Specify a one-line watchpoint command inline. Be "
+ "sure to surround it with quotes.">;
+ def watchpoint_command_add_stop_on_error
+ : Option<"stop-on-error", "e">,
+ Arg<"Boolean">,
+ Desc<"Specify whether watchpoint command execution should "
+ "terminate on error.">;
+ def watchpoint_command_add_script_type
+ : Option<"script-type", "s">,
+ EnumArg<"ScriptLang">,
+ Desc<"Specify the language for the"
+ " commands - if none is specified, the lldb command interpreter "
+ "will be "
+ "used.">;
+ def watchpoint_command_add_python_function
+ : Option<"python-function", "F">,
+ Group<2>,
+ Arg<"PythonFunction">,
+ Desc<"Give the name of a Python function "
+ "to run as command for this watchpoint. Be sure to give a module "
+ "name if "
+ "appropriate.">;
}
let Command = "watchpoint delete" in {
- def watchpoint_delete_force : Option<"force", "f">, Group<1>,
- Desc<"Delete all watchpoints without querying for confirmation.">;
+ def watchpoint_delete_force
+ : Option<"force", "f">,
+ Group<1>,
+ Desc<"Delete all watchpoints without querying for confirmation.">;
}
let Command = "trace load" in {
- def trace_load_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Show verbose trace load logging for debugging the plug-in "
- "implementation.">;
+ def trace_load_verbose
+ : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Show verbose trace load logging for debugging the plug-in "
+ "implementation.">;
}
let Command = "trace save" in {
- def trace_save_compact: Option<"compact", "c">,
- Group<1>,
- Desc<"Try not to save to disk information irrelevant to the traced "
- "processes. Each trace plug-in implements this in a different "
- "fashion.">;
+ def trace_save_compact
+ : Option<"compact", "c">,
+ Group<1>,
+ Desc<"Try not to save to disk information irrelevant to the traced "
+ "processes. Each trace plug-in implements this in a different "
+ "fashion.">;
}
let Command = "trace dump" in {
- def trace_dump_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Show verbose trace information.">;
+ def trace_dump_verbose : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Show verbose trace information.">;
}
let Command = "trace schema" in {
- def trace_schema_verbose : Option<"verbose", "v">, Group<1>,
- Desc<"Show verbose trace schema logging for debugging the plug-in.">;
+ def trace_schema_verbose
+ : Option<"verbose", "v">,
+ Group<1>,
+ Desc<"Show verbose trace schema logging for debugging the plug-in.">;
}
let Command = "statistics dump" in {
- def statistics_dump_all: Option<"all-targets", "a">, Group<1>,
- Desc<"Include statistics for all targets.">;
- def statistics_dump_summary: Option<"summary", "s">, Group<1>,
- Desc<"Dump only high-level summary statistics. "
- "Exclude targets, modules, breakpoints etc... details.">;
- def statistics_dump_force: Option<"load-all-debug-info", "f">, Group<1>,
- Desc<"Dump the total possible debug info statistics. "
- "Force loading all the debug information if not yet loaded, and collect "
- "statistics with those.">;
- def statistics_dump_targets: Option<"targets", "r">, Group<1>,
- Arg<"Boolean">,
- Desc<"Dump statistics for the targets, including breakpoints, expression "
- "evaluations, frame variables, etc. "
- "Defaults to true in both default mode and summary mode. "
- "In default mode, if both '--targets' and '--modules' are 'true', a list "
- "of module identifiers will be added to the 'targets' section.">;
- def statistics_dump_modules: Option<"modules", "m">, Group<1>,
- Arg<"Boolean">,
- Desc<"Dump statistics for the modules, including time and size of various "
- "aspects of the module and debug information, type system, path, etc. "
- "Defaults to true, unless the '--summary' mode is enabled, in which case "
- "this is turned off unless specified. "
- "In default mode, if both '--targets' and '--modules' are 'true', a list "
- "of module identifiers will be added to the 'targets' section.">;
+ def statistics_dump_all : Option<"all-targets", "a">,
+ Group<1>,
+ Desc<"Include statistics for all targets.">;
+ def statistics_dump_summary
+ : Option<"summary", "s">,
+ Group<1>,
+ Desc<"Dump only high-level summary statistics. Exclude targets, "
+ "modules, breakpoints etc... details.">;
+ def statistics_dump_force
+ : Option<"load-all-debug-info", "f">,
+ Group<1>,
+ Desc<"Dump the total possible debug info statistics. Force loading all "
+ "the debug information if not yet loaded, and collect statistics "
+ "with those.">;
+ def statistics_dump_targets
+ : Option<"targets", "r">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Dump statistics for the targets, including breakpoints, "
+ "expression evaluations, frame variables, etc. Defaults to true "
+ "in both default mode and summary mode. In default mode, if both "
+ "'--targets' and '--modules' are 'true', a list of module "
+ "identifiers will be added to the 'targets' section.">;
+ def statistics_dump_modules
+ : Option<"modules", "m">,
+ Group<1>,
+ Arg<"Boolean">,
+ Desc<"Dump statistics for the modules, including time and size of "
+ "various aspects of the module and debug information, type "
+ "system, path, etc. Defaults to true, unless the '--summary' mode "
+ "is enabled, in which case this is turned off unless specified. "
+ "In default mode, if both '--targets' and '--modules' are 'true', "
+ "a list of module identifiers will be added to the 'targets' "
+ "section.">;
def statistics_dump_transcript
: Option<"transcript", "t">,
Group<1>,
Arg<"Boolean">,
Desc<"If the setting interpreter.save-transcript is enabled and this "
"option is 'true', include a JSON array with all commands the "
- "user and/or "
- "scripts executed during a debug session. "
- "Defaults to false. ">;
+ "user and/or scripts executed during a debug session. Defaults to "
+ "false. ">;
def statistics_dump_plugins
: Option<"plugins", "p">,
Group<1>,
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index 53dd333f045c..fda34a8ad263 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -59,7 +59,7 @@ let Definition = "debugger" in {
Desc<"The default disassembly format string to use when disassembling instruction sequences.">;
def FrameFormat: Property<"frame-format", "FormatEntity">,
Global,
- DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
+ DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
Desc<"The default frame format string to use when displaying stack frame information for threads.">;
def NotiftVoid: Property<"notify-void", "Boolean">,
Global,
@@ -233,7 +233,7 @@ let Definition = "debugger" in {
Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">;
def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">,
Global,
- DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
+ DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}${frame.kind}{${function.is-optimized} [opt]}{${function.is-inlined} [inlined]}{${frame.is-artificial} [artificial]}\\\\n">,
Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">;
def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">,
Global,
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index e0a7d6934570..f2ed1f739534 100644
--- a/lldb/source/Core/Disassembler.cpp
+++ b/lldb/source/Core/Disassembler.cpp
@@ -26,7 +26,11 @@
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/Variable.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
@@ -41,6 +45,8 @@
#include "lldb/lldb-private-enumerations.h"
#include "lldb/lldb-private-interfaces.h"
#include "lldb/lldb-private-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/TargetParser/Triple.h"
@@ -280,6 +286,127 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine(
return false;
}
+// For each instruction, this block attempts to resolve in-scope variables
+// and determine if the current PC falls within their
+// DWARF location entry. If so, it prints a simplified annotation using the
+// variable name and its resolved location (e.g., "var = reg; " ).
+//
+// Annotations are only included if the variable has a valid DWARF location
+// entry, and the location string is non-empty after filtering. Decoding
+// errors and DWARF opcodes are intentionally omitted to keep the output
+// concise and user-friendly.
+//
+// The goal is to give users helpful live variable hints alongside the
+// disassembled instruction stream, similar to how debug information
+// enhances source-level debugging.
+std::vector<std::string>
+VariableAnnotator::annotate(Instruction &inst, Target &target,
+ const lldb::ModuleSP &module_sp) {
+ std::vector<std::string> events;
+
+ // If we lost module context, everything becomes <undef>.
+ if (!module_sp) {
+ for (const auto &KV : Live_)
+ events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
+ Live_.clear();
+ return events;
+ }
+
+ // Resolve function/block at this *file* address.
+ SymbolContext sc;
+ const Address &iaddr = inst.GetAddress();
+ const auto mask = eSymbolContextFunction | eSymbolContextBlock;
+ if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) ||
+ !sc.function) {
+ // No function context: everything dies here.
+ for (const auto &KV : Live_)
+ events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
+ Live_.clear();
+ return events;
+ }
+
+ // Collect in-scope variables for this instruction into Current.
+ VariableList var_list;
+ // Innermost block containing iaddr.
+ if (Block *B = sc.block) {
+ auto filter = [](Variable *v) -> bool { return v && !v->IsArtificial(); };
+ B->AppendVariables(/*can_create*/ true,
+ /*get_parent_variables*/ true,
+ /*stop_if_block_is_inlined_function*/ false,
+ /*filter*/ filter,
+ /*variable_list*/ &var_list);
+ }
+
+ const lldb::addr_t pc_file = iaddr.GetFileAddress();
+ const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress();
+
+ // ABI from Target (pretty reg names if plugin exists). Safe to be null.
+ lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, target.GetArchitecture());
+ ABI *abi = abi_sp.get();
+
+ llvm::DIDumpOptions opts;
+ opts.ShowAddresses = false;
+ // Prefer "register-only" output when we have an ABI.
+ opts.PrintRegisterOnly = static_cast<bool>(abi_sp);
+
+ llvm::DenseMap<lldb::user_id_t, VarState> Current;
+
+ for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) {
+ lldb::VariableSP v = var_list.GetVariableAtIndex(i);
+ if (!v || v->IsArtificial())
+ continue;
+
+ const char *nm = v->GetName().AsCString();
+ llvm::StringRef name = nm ? nm : "<anon>";
+
+ DWARFExpressionList &exprs = v->LocationExpressionList();
+ if (!exprs.IsValid())
+ continue;
+
+ auto entry_or_err = exprs.GetExpressionEntryAtAddress(func_file, pc_file);
+ if (!entry_or_err)
+ continue;
+
+ auto entry = *entry_or_err;
+
+ StreamString loc_ss;
+ entry.expr->DumpLocation(&loc_ss, eDescriptionLevelBrief, abi, opts);
+
+ llvm::StringRef loc = llvm::StringRef(loc_ss.GetString()).trim();
+ if (loc.empty())
+ continue;
+
+ Current.try_emplace(v->GetID(),
+ VarState{std::string(name), std::string(loc)});
+ }
+
+ // Diff Live_ → Current.
+
+ // 1) Starts/changes: iterate Current and compare with Live_.
+ for (const auto &KV : Current) {
+ auto it = Live_.find(KV.first);
+ if (it == Live_.end()) {
+ // Newly live.
+ events.emplace_back(
+ llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str());
+ } else if (it->second.last_loc != KV.second.last_loc) {
+ // Location changed.
+ events.emplace_back(
+ llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str());
+ }
+ }
+
+ // 2) Ends: anything that was live but is not in Current becomes <undef>.
+ for (const auto &KV : Live_) {
+ if (!Current.count(KV.first))
+ events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str());
+ }
+
+ // Commit new state.
+ Live_ = std::move(Current);
+ return events;
+}
+
void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const ExecutionContext &exe_ctx,
bool mixed_source_and_assembly,
@@ -376,6 +503,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
}
}
+ VariableAnnotator annot;
previous_symbol = nullptr;
SourceLine previous_line;
for (size_t i = 0; i < num_instructions_found; ++i) {
@@ -540,10 +668,26 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
const bool show_bytes = (options & eOptionShowBytes) != 0;
const bool show_control_flow_kind =
(options & eOptionShowControlFlowKind) != 0;
- inst->Dump(&strm, max_opcode_byte_size, true, show_bytes,
+
+ StreamString inst_line;
+
+ inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes,
show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
address_text_size);
+
+ if ((options & eOptionVariableAnnotations) && target_sp) {
+ auto annotations = annot.annotate(*inst, *target_sp, module_sp);
+ if (!annotations.empty()) {
+ const size_t annotation_column = 100;
+ inst_line.FillLastLineToColumn(annotation_column, ' ');
+ inst_line.PutCString("; ");
+ inst_line.PutCString(llvm::join(annotations, ", "));
+ }
+ }
+
+ strm.PutCString(inst_line.GetString());
strm.EOL();
+
} else {
break;
}
@@ -724,9 +868,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) {
return false;
}
-bool Instruction::CanSetBreakpoint () {
- return !HasDelaySlot();
-}
+bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); }
bool Instruction::HasDelaySlot() {
// Default is false.
@@ -1073,10 +1215,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) {
m_instructions.push_back(inst_sp);
}
-uint32_t
-InstructionList::GetIndexOfNextBranchInstruction(uint32_t start,
- bool ignore_calls,
- bool *found_calls) const {
+uint32_t InstructionList::GetIndexOfNextBranchInstruction(
+ uint32_t start, bool ignore_calls, bool *found_calls) const {
size_t num_instructions = m_instructions.size();
uint32_t next_branch = UINT32_MAX;
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index 5d3c8b421d5d..491f5c6320d9 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -108,6 +108,7 @@ constexpr Definition g_frame_child_entries[] = {
Entry::DefinitionWithChildren("reg", EntryType::FrameRegisterByName,
g_string_entry),
Definition("is-artificial", EntryType::FrameIsArtificial),
+ Definition("kind", EntryType::FrameKind),
};
constexpr Definition g_function_child_entries[] = {
@@ -380,6 +381,7 @@ const char *FormatEntity::Entry::TypeToCString(Type t) {
ENUM_TO_CSTR(FrameRegisterFlags);
ENUM_TO_CSTR(FrameRegisterByName);
ENUM_TO_CSTR(FrameIsArtificial);
+ ENUM_TO_CSTR(FrameKind);
ENUM_TO_CSTR(ScriptFrame);
ENUM_TO_CSTR(FunctionID);
ENUM_TO_CSTR(FunctionDidChange);
@@ -1679,7 +1681,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
StackFrame *frame = exe_ctx->GetFramePtr();
if (frame) {
const Address &pc_addr = frame->GetFrameCodeAddress();
- if (pc_addr.IsValid()) {
+ if (pc_addr.IsValid() || frame->IsSynthetic()) {
if (DumpAddressAndContent(s, sc, exe_ctx, pc_addr, false))
return true;
}
@@ -1747,6 +1749,18 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
return false;
}
+ case Entry::Type::FrameKind: {
+ if (exe_ctx)
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
+ if (frame->IsSynthetic())
+ s.PutCString(" [synthetic]");
+ else if (frame->IsHistorical())
+ s.PutCString(" [history]");
+ return true;
+ }
+ return false;
+ }
+
case Entry::Type::ScriptFrame:
if (exe_ctx) {
StackFrame *frame = exe_ctx->GetFramePtr();
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index ce4db4e0daa8..91b9c0007617 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -556,3 +556,18 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
break;
}
}
+
+ConstString Mangled::GetBaseName() const {
+ const auto &demangled_info = GetDemangledInfo();
+ if (!demangled_info.has_value())
+ return {};
+
+ ConstString demangled_name = GetDemangledName();
+ if (!demangled_name)
+ return {};
+
+ const char *name_str = demangled_name.AsCString();
+ const auto &range = demangled_info->BasenameRange;
+ return ConstString(
+ llvm::StringRef(name_str + range.first, range.second - range.first));
+}
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index 060732f421f8..bc63a41c90d1 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -844,8 +844,6 @@ public:
}
bool RemoveIfOrphaned(const Module *module_ptr) {
- if (!module_ptr)
- return false;
std::lock_guard<std::recursive_mutex> guard(GetMutex());
RemoveFromMap(*module_ptr, /*if_orphaned=*/true);
return m_list.RemoveIfOrphaned(module_ptr);
@@ -982,7 +980,7 @@ private:
};
struct SharedModuleListInfo {
- SharedModuleList module_list;
+ ModuleList module_list;
ModuleListProperties module_list_properties;
};
}
@@ -1000,7 +998,7 @@ static SharedModuleListInfo &GetSharedModuleListInfo()
return *g_shared_module_list_info;
}
-static SharedModuleList &GetSharedModuleList() {
+static ModuleList &GetSharedModuleList() {
return GetSharedModuleListInfo().module_list;
}
@@ -1010,8 +1008,8 @@ ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
bool ModuleList::ModuleIsInCache(const Module *module_ptr) {
if (module_ptr) {
- SharedModuleList &shared_module_list = GetSharedModuleList();
- return shared_module_list.FindModule(*module_ptr).get() != nullptr;
+ ModuleList &shared_module_list = GetSharedModuleList();
+ return shared_module_list.FindModule(module_ptr).get() != nullptr;
}
return false;
}
@@ -1034,8 +1032,9 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
bool *did_create_ptr, bool always_create) {
- SharedModuleList &shared_module_list = GetSharedModuleList();
- std::lock_guard<std::recursive_mutex> guard(shared_module_list.GetMutex());
+ ModuleList &shared_module_list = GetSharedModuleList();
+ std::lock_guard<std::recursive_mutex> guard(
+ shared_module_list.m_modules_mutex);
char path[PATH_MAX];
Status error;
diff --git a/lldb/source/Core/ProtocolServer.cpp b/lldb/source/Core/ProtocolServer.cpp
index 41636cdacdec..38668f39795a 100644
--- a/lldb/source/Core/ProtocolServer.cpp
+++ b/lldb/source/Core/ProtocolServer.cpp
@@ -8,24 +8,29 @@
#include "lldb/Core/ProtocolServer.h"
#include "lldb/Core/PluginManager.h"
+#include "llvm/Support/Error.h"
using namespace lldb_private;
using namespace lldb;
-ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) {
- static std::mutex g_mutex;
+static std::pair<llvm::StringMap<ProtocolServerUP> &, std::mutex &> Servers() {
static llvm::StringMap<ProtocolServerUP> g_protocol_server_instances;
+ static std::mutex g_mutex;
+ return {g_protocol_server_instances, g_mutex};
+}
+
+ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) {
+ auto [protocol_server_instances, mutex] = Servers();
- std::lock_guard<std::mutex> guard(g_mutex);
+ std::lock_guard<std::mutex> guard(mutex);
- auto it = g_protocol_server_instances.find(name);
- if (it != g_protocol_server_instances.end())
+ auto it = protocol_server_instances.find(name);
+ if (it != protocol_server_instances.end())
return it->second.get();
if (ProtocolServerCreateInstance create_callback =
PluginManager::GetProtocolCreateCallbackForPluginName(name)) {
- auto pair =
- g_protocol_server_instances.try_emplace(name, create_callback());
+ auto pair = protocol_server_instances.try_emplace(name, create_callback());
return pair.first->second.get();
}
@@ -45,3 +50,18 @@ std::vector<llvm::StringRef> ProtocolServer::GetSupportedProtocols() {
return supported_protocols;
}
+
+llvm::Error ProtocolServer::Terminate() {
+ llvm::Error error = llvm::Error::success();
+
+ auto [protocol_server_instances, mutex] = Servers();
+ std::lock_guard<std::mutex> guard(mutex);
+ for (auto &instance : protocol_server_instances) {
+ if (llvm::Error instance_error = instance.second->Stop())
+ error = llvm::joinErrors(std::move(error), std::move(instance_error));
+ }
+
+ protocol_server_instances.clear();
+
+ return error;
+}
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index ed4e4e4e341a..332cf2c86024 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value,
}
void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
- ABI *abi) const {
+ ABI *abi,
+ llvm::DIDumpOptions options) const {
auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr;
auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum,
bool IsEH) -> llvm::StringRef {
@@ -79,10 +80,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level,
return llvm::StringRef(RegName);
return {};
};
- llvm::DIDumpOptions DumpOpts;
- DumpOpts.GetNameForDWARFReg = GetRegName;
+ options.GetNameForDWARFReg = GetRegName;
llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize());
- llvm::printDwarfExpression(&E, s->AsRawOstream(), DumpOpts, nullptr);
+ llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr);
}
RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; }
@@ -2078,7 +2078,7 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
case DW_OP_implicit_pointer: {
dwarf4_location_description_kind = Implicit;
- return llvm::createStringError("Could not evaluate %s.",
+ return llvm::createStringError("could not evaluate %s",
DW_OP_value_to_name(op));
}
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index ef7333518f00..91c174013603 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -254,7 +254,7 @@ llvm::Expected<Value> DWARFExpressionList::Evaluate(
}
if (!pc.IsValid()) {
- return llvm::createStringError("Invalid PC in frame.");
+ return llvm::createStringError("invalid PC in frame");
}
addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr());
const DWARFExpression *entry =
diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp
index 796851ff15ca..16ecb1d7deef 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
llvm::Expected<FunctionCallLabel>
lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
- llvm::SmallVector<llvm::StringRef, 4> components;
- label.split(components, ":", /*MaxSplit=*/3);
+ llvm::SmallVector<llvm::StringRef, 5> components;
+ label.split(components, ":", /*MaxSplit=*/4);
- if (components.size() != 4)
+ if (components.size() != 5)
return llvm::createStringError("malformed function call label.");
if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
"expected function call label prefix '{0}' but found '{1}' instead.",
FunctionCallLabelPrefix, components[0]));
- llvm::StringRef module_label = components[1];
- llvm::StringRef die_label = components[2];
+ llvm::StringRef discriminator = components[1];
+ llvm::StringRef module_label = components[2];
+ llvm::StringRef die_label = components[3];
+ llvm::StringRef lookup_name = components[4];
lldb::user_id_t module_id = 0;
if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
return llvm::createStringError(
llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
- return FunctionCallLabel{/*.module_id=*/module_id,
+ return FunctionCallLabel{/*.discriminator=*/discriminator,
+ /*.module_id=*/module_id,
/*.symbol_id=*/die_id,
- /*.lookup_name=*/components[3]};
+ /*.lookup_name=*/lookup_name};
}
std::string lldb_private::FunctionCallLabel::toString() const {
- return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
- module_id, symbol_id, lookup_name)
+ return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+ discriminator, module_id, symbol_id, lookup_name)
.str();
}
void llvm::format_provider<FunctionCallLabel>::format(
const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
- OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
- "lookup_name: {2} }",
- label.module_id, label.symbol_id, label.lookup_name);
+ OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+ "{1:x}, symbol_id: {2:x}, "
+ "lookup_name: {3} }",
+ label.discriminator, label.module_id, label.symbol_id,
+ label.lookup_name);
}
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index d557084acb74..25d4a87b89ef 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -543,62 +543,7 @@ lldb::SectionType IRExecutionUnit::GetSectionTypeFromSectionName(
else if (name.starts_with("__debug_") || name.starts_with(".debug_")) {
const uint32_t name_idx = name[0] == '_' ? 8 : 7;
llvm::StringRef dwarf_name(name.substr(name_idx));
- switch (dwarf_name[0]) {
- case 'a':
- if (dwarf_name == "abbrev")
- sect_type = lldb::eSectionTypeDWARFDebugAbbrev;
- else if (dwarf_name == "aranges")
- sect_type = lldb::eSectionTypeDWARFDebugAranges;
- else if (dwarf_name == "addr")
- sect_type = lldb::eSectionTypeDWARFDebugAddr;
- break;
-
- case 'f':
- if (dwarf_name == "frame")
- sect_type = lldb::eSectionTypeDWARFDebugFrame;
- break;
-
- case 'i':
- if (dwarf_name == "info")
- sect_type = lldb::eSectionTypeDWARFDebugInfo;
- break;
-
- case 'l':
- if (dwarf_name == "line")
- sect_type = lldb::eSectionTypeDWARFDebugLine;
- else if (dwarf_name == "loc")
- sect_type = lldb::eSectionTypeDWARFDebugLoc;
- else if (dwarf_name == "loclists")
- sect_type = lldb::eSectionTypeDWARFDebugLocLists;
- break;
-
- case 'm':
- if (dwarf_name == "macinfo")
- sect_type = lldb::eSectionTypeDWARFDebugMacInfo;
- break;
-
- case 'p':
- if (dwarf_name == "pubnames")
- sect_type = lldb::eSectionTypeDWARFDebugPubNames;
- else if (dwarf_name == "pubtypes")
- sect_type = lldb::eSectionTypeDWARFDebugPubTypes;
- break;
-
- case 's':
- if (dwarf_name == "str")
- sect_type = lldb::eSectionTypeDWARFDebugStr;
- else if (dwarf_name == "str_offsets")
- sect_type = lldb::eSectionTypeDWARFDebugStrOffsets;
- break;
-
- case 'r':
- if (dwarf_name == "ranges")
- sect_type = lldb::eSectionTypeDWARFDebugRanges;
- break;
-
- default:
- break;
- }
+ sect_type = ObjectFile::GetDWARFSectionTypeFromName(dwarf_name);
} else if (name.starts_with("__apple_") || name.starts_with(".apple_"))
sect_type = lldb::eSectionTypeInvalid;
else if (name == "__objc_imageinfo")
@@ -778,7 +723,7 @@ private:
/// Returns address of the function referred to by the special function call
/// label \c label.
static llvm::Expected<lldb::addr_t>
-ResolveFunctionCallLabel(const FunctionCallLabel &label,
+ResolveFunctionCallLabel(FunctionCallLabel &label,
const lldb_private::SymbolContext &sc,
bool &symbol_was_missing_weak) {
symbol_was_missing_weak = false;
diff --git a/lldb/source/Expression/IRMemoryMap.cpp b/lldb/source/Expression/IRMemoryMap.cpp
index 150699352a2e..f978217fa8f2 100644
--- a/lldb/source/Expression/IRMemoryMap.cpp
+++ b/lldb/source/Expression/IRMemoryMap.cpp
@@ -637,10 +637,19 @@ void IRMemoryMap::WriteScalarToMemory(lldb::addr_t process_address,
}
void IRMemoryMap::WritePointerToMemory(lldb::addr_t process_address,
- lldb::addr_t address, Status &error) {
+ lldb::addr_t pointer, Status &error) {
error.Clear();
- Scalar scalar(address);
+ /// Only ask the Process to fix `pointer` if the address belongs to the
+ /// process. An address belongs to the process if the Allocation policy is not
+ /// eAllocationPolicyHostOnly.
+ auto it = FindAllocation(pointer, 1);
+ if (it == m_allocations.end() ||
+ it->second.m_policy != AllocationPolicy::eAllocationPolicyHostOnly)
+ if (auto process_sp = GetProcessWP().lock())
+ pointer = process_sp->FixAnyAddress(pointer);
+
+ Scalar scalar(pointer);
WriteScalarToMemory(process_address, scalar, GetAddressByteSize(), error);
}
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 5ed30fbb231d..1b1922e71076 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -10,10 +10,8 @@
#include <iomanip>
#include <optional>
-#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/Editline.h"
-#include "lldb/Host/FileSystem.h"
-#include "lldb/Host/Host.h"
+#include "lldb/Host/HostInfo.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/CompletionRequest.h"
@@ -100,11 +98,6 @@ bool IsOnlySpaces(const EditLineStringType &content) {
return true;
}
-static size_t ColumnWidth(llvm::StringRef str) {
- std::string stripped = ansi::StripAnsiTerminalCodes(str);
- return llvm::sys::locale::columnWidth(stripped);
-}
-
static int GetOperation(HistoryOperation op) {
// The naming used by editline for the history operations is counter
// intuitive to how it's used in LLDB's editline implementation.
@@ -219,20 +212,19 @@ private:
const char *GetHistoryFilePath() {
// Compute the history path lazily.
if (m_path.empty() && m_history && !m_prefix.empty()) {
- llvm::SmallString<128> lldb_history_file;
- FileSystem::Instance().GetHomeDirectory(lldb_history_file);
- llvm::sys::path::append(lldb_history_file, ".lldb");
+ FileSpec lldb_dir = HostInfo::GetUserLLDBDir();
// LLDB stores its history in ~/.lldb/. If for some reason this directory
// isn't writable or cannot be created, history won't be available.
- if (!llvm::sys::fs::create_directory(lldb_history_file)) {
+ if (!llvm::sys::fs::create_directory(lldb_dir.GetPath())) {
#if LLDB_EDITLINE_USE_WCHAR
std::string filename = m_prefix + "-widehistory";
#else
std::string filename = m_prefix + "-history";
#endif
- llvm::sys::path::append(lldb_history_file, filename);
- m_path = std::string(lldb_history_file.str());
+ FileSpec lldb_history_file =
+ lldb_dir.CopyByAppendingPathComponent(filename);
+ m_path = lldb_history_file.GetPath();
}
}
@@ -332,8 +324,8 @@ std::string Editline::PromptForIndex(int line_index) {
if (m_set_continuation_prompt.length() > 0) {
continuation_prompt = m_set_continuation_prompt;
// Ensure that both prompts are the same length through space padding
- const size_t prompt_width = ColumnWidth(prompt);
- const size_t cont_prompt_width = ColumnWidth(continuation_prompt);
+ const size_t prompt_width = ansi::ColumnWidth(prompt);
+ const size_t cont_prompt_width = ansi::ColumnWidth(continuation_prompt);
const size_t padded_prompt_width =
std::max(prompt_width, cont_prompt_width);
if (prompt_width < padded_prompt_width)
@@ -358,7 +350,9 @@ void Editline::SetCurrentLine(int line_index) {
m_current_prompt = PromptForIndex(line_index);
}
-size_t Editline::GetPromptWidth() { return ColumnWidth(PromptForIndex(0)); }
+size_t Editline::GetPromptWidth() {
+ return ansi::ColumnWidth(PromptForIndex(0));
+}
bool Editline::IsEmacs() {
const char *editor;
@@ -448,7 +442,7 @@ void Editline::DisplayInput(int firstIndex) {
int Editline::CountRowsForLine(const EditLineStringType &content) {
std::string prompt =
PromptForIndex(0); // Prompt width is constant during an edit session
- int line_length = (int)(content.length() + ColumnWidth(prompt));
+ int line_length = (int)(content.length() + ansi::ColumnWidth(prompt));
return (line_length / m_terminal_width) + 1;
}
diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp
index 23b6dc9fe850..8fd1ca069dc0 100644
--- a/lldb/source/Host/common/File.cpp
+++ b/lldb/source/Host/common/File.cpp
@@ -36,6 +36,7 @@
#include "llvm/Support/Errno.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Process.h"
+#include "llvm/Support/raw_ostream.h"
using namespace lldb;
using namespace lldb_private;
@@ -247,6 +248,32 @@ uint32_t File::GetPermissions(Status &error) const {
return file_stats.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
}
+NativeFile::NativeFile() = default;
+
+NativeFile::NativeFile(FILE *fh, bool transfer_ownership)
+ : m_stream(fh), m_own_stream(transfer_ownership) {
+#ifdef _WIN32
+ // In order to properly display non ASCII characters in Windows, we need to
+ // use Windows APIs to print to the console. This is only required if the
+ // stream outputs to a console.
+ int fd = _fileno(fh);
+ is_windows_console =
+ ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
+#endif
+}
+
+NativeFile::NativeFile(int fd, OpenOptions options, bool transfer_ownership)
+ : m_descriptor(fd), m_own_descriptor(transfer_ownership),
+ m_options(options) {
+#ifdef _WIN32
+ // In order to properly display non ASCII characters in Windows, we need to
+ // use Windows APIs to print to the console. This is only required if the
+ // file outputs to a console.
+ is_windows_console =
+ ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
+#endif
+}
+
bool NativeFile::IsValid() const {
std::scoped_lock<std::mutex, std::mutex> lock(m_descriptor_mutex, m_stream_mutex);
return DescriptorIsValidUnlocked() || StreamIsValidUnlocked();
@@ -629,6 +656,13 @@ Status NativeFile::Write(const void *buf, size_t &num_bytes) {
}
if (ValueGuard stream_guard = StreamIsValid()) {
+#ifdef _WIN32
+ if (is_windows_console) {
+ llvm::raw_fd_ostream(_fileno(m_stream), false)
+ .write((char *)buf, num_bytes);
+ return error;
+ }
+#endif
bytes_written = ::fwrite(buf, 1, num_bytes, m_stream);
if (bytes_written == 0) {
diff --git a/lldb/source/Host/common/HostInfoBase.cpp b/lldb/source/Host/common/HostInfoBase.cpp
index 89dfe4a9e9ba..a02ac77df66a 100644
--- a/lldb/source/Host/common/HostInfoBase.cpp
+++ b/lldb/source/Host/common/HostInfoBase.cpp
@@ -61,6 +61,10 @@ struct HostInfoBaseFields {
FileSpec m_lldb_clang_resource_dir;
llvm::once_flag m_lldb_system_plugin_dir_once;
FileSpec m_lldb_system_plugin_dir;
+ llvm::once_flag m_lldb_user_home_dir_once;
+ FileSpec m_lldb_user_home_dir;
+ llvm::once_flag m_lldb_user_lldb_dir_once;
+ FileSpec m_lldb_user_lldb_dir;
llvm::once_flag m_lldb_user_plugin_dir_once;
FileSpec m_lldb_user_plugin_dir;
llvm::once_flag m_lldb_process_tmp_dir_once;
@@ -161,6 +165,26 @@ FileSpec HostInfoBase::GetSystemPluginDir() {
return g_fields->m_lldb_system_plugin_dir;
}
+FileSpec HostInfoBase::GetUserHomeDir() {
+ llvm::call_once(g_fields->m_lldb_user_home_dir_once, []() {
+ if (!HostInfo::ComputeUserHomeDirectory(g_fields->m_lldb_user_home_dir))
+ g_fields->m_lldb_user_home_dir = FileSpec();
+ LLDB_LOG(GetLog(LLDBLog::Host), "user home dir -> `{0}`",
+ g_fields->m_lldb_user_home_dir);
+ });
+ return g_fields->m_lldb_user_home_dir;
+}
+
+FileSpec HostInfoBase::GetUserLLDBDir() {
+ llvm::call_once(g_fields->m_lldb_user_lldb_dir_once, []() {
+ if (!HostInfo::ComputeUserLLDBHomeDirectory(g_fields->m_lldb_user_lldb_dir))
+ g_fields->m_lldb_user_lldb_dir = FileSpec();
+ LLDB_LOG(GetLog(LLDBLog::Host), "user lldb home dir -> `{0}`",
+ g_fields->m_lldb_user_lldb_dir);
+ });
+ return g_fields->m_lldb_user_lldb_dir;
+}
+
FileSpec HostInfoBase::GetUserPluginDir() {
llvm::call_once(g_fields->m_lldb_user_plugin_dir_once, []() {
if (!HostInfo::ComputeUserPluginsDirectory(
@@ -316,6 +340,20 @@ bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
return false;
}
+bool HostInfoBase::ComputeUserHomeDirectory(FileSpec &file_spec) {
+ FileSpec temp_file("~");
+ FileSystem::Instance().Resolve(temp_file);
+ file_spec.SetDirectory(temp_file.GetPathAsConstString());
+ return true;
+}
+
+bool HostInfoBase::ComputeUserLLDBHomeDirectory(FileSpec &file_spec) {
+ FileSpec home_dir_spec = GetUserHomeDir();
+ home_dir_spec.AppendPathComponent(".lldb");
+ file_spec.SetDirectory(home_dir_spec.GetPathAsConstString());
+ return true;
+}
+
bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) {
// TODO(zturner): Figure out how to compute the user plugins directory for
// all platforms.
diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index 61f94190c956..79e1322a870e 100644
--- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -39,7 +39,7 @@
#include <Foundation/Foundation.h>
#include <mach-o/dyld.h>
#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
- MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0
#if __has_include(<mach-o/dyld_introspection.h>)
#include <mach-o/dyld_introspection.h>
#define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS
@@ -78,8 +78,8 @@ std::optional<std::string> HostInfoMacOSX::GetOSBuildString() {
static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) {
@autoreleasepool {
NSDictionary *version_info =
- [NSDictionary dictionaryWithContentsOfFile:
- @"/System/Library/CoreServices/SystemVersion.plist"];
+ [NSDictionary dictionaryWithContentsOfFile:
+ @"/System/Library/CoreServices/SystemVersion.plist"];
NSString *version_value = [version_info objectForKey: Key];
const char *version_str = [version_value UTF8String];
version.tryParse(version_str);
@@ -225,9 +225,9 @@ bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) {
}
bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) {
- FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns");
- FileSystem::Instance().Resolve(temp_file);
- file_spec.SetDirectory(temp_file.GetPathAsConstString());
+ FileSpec home_dir_spec = GetUserHomeDir();
+ home_dir_spec.AppendPathComponent("Library/Application Support/LLDB/PlugIns");
+ file_spec.SetDirectory(home_dir_spec.GetPathAsConstString());
return true;
}
diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp
index 4277b8edb38e..e8973a3fb937 100644
--- a/lldb/source/Host/windows/Host.cpp
+++ b/lldb/source/Host/windows/Host.cpp
@@ -22,10 +22,8 @@
#include "lldb/Utility/StreamString.h"
#include "lldb/Utility/StructuredData.h"
-#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/ConvertUTF.h"
-#include "llvm/Support/ManagedStatic.h"
// Windows includes
#include <tlhelp32.h>
@@ -308,52 +306,28 @@ Environment Host::GetEnvironment() {
return env;
}
-/// Manages the lifecycle of a Windows Event's Source.
-/// The destructor will call DeregisterEventSource.
-/// This class is meant to be used with \ref llvm::ManagedStatic.
-class WindowsEventLog {
-public:
- WindowsEventLog() : handle(RegisterEventSource(nullptr, L"lldb")) {}
-
- ~WindowsEventLog() {
- if (handle)
- DeregisterEventSource(handle);
- }
-
- HANDLE GetHandle() const { return handle; }
-
-private:
- HANDLE handle;
-};
-
-static llvm::ManagedStatic<WindowsEventLog> event_log;
-
void Host::SystemLog(Severity severity, llvm::StringRef message) {
if (message.empty())
return;
- HANDLE h = event_log->GetHandle();
- if (!h)
- return;
-
- llvm::SmallVector<wchar_t, 1> argsUTF16;
- if (UTF8ToUTF16(message.str(), argsUTF16))
- return;
+ std::string log_msg;
+ llvm::raw_string_ostream stream(log_msg);
- WORD event_type;
switch (severity) {
case lldb::eSeverityWarning:
- event_type = EVENTLOG_WARNING_TYPE;
+ stream << "[Warning] ";
break;
case lldb::eSeverityError:
- event_type = EVENTLOG_ERROR_TYPE;
+ stream << "[Error] ";
break;
case lldb::eSeverityInfo:
default:
- event_type = EVENTLOG_INFORMATION_TYPE;
+ stream << "[Info] ";
+ break;
}
- LPCWSTR messages[1] = {argsUTF16.data()};
- ReportEventW(h, event_type, 0, 0, nullptr, std::size(messages), 0, messages,
- nullptr);
+ stream << message;
+ stream.flush();
+
+ OutputDebugStringA(log_msg.c_str());
}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 650b754fd8ac..d909c5650c95 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -52,6 +52,7 @@
#include "lldb/Core/Telemetry.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Utility/ErrorMessages.h"
+#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/State.h"
@@ -1801,13 +1802,13 @@ CommandObject *CommandInterpreter::BuildAliasResult(
// Make sure we aren't going outside the bounds of the cmd string:
if (strpos < start_fudge) {
- result.AppendError("Unmatched quote at command beginning.");
+ result.AppendError("unmatched quote at command beginning");
return nullptr;
}
llvm::StringRef arg_text = entry.ref();
if (strpos - start_fudge + arg_text.size() + len_fudge >
raw_input_string.size()) {
- result.AppendError("Unmatched quote at command end.");
+ result.AppendError("unmatched quote at command end");
return nullptr;
}
raw_input_string = raw_input_string.erase(
@@ -2090,7 +2091,7 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
command_string = command_line;
original_command_string = command_line;
if (m_repeat_command.empty()) {
- result.AppendError("No auto repeat.");
+ result.AppendError("no auto repeat");
return false;
}
@@ -2502,22 +2503,18 @@ int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) {
return position;
}
-static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file,
- llvm::StringRef suffix = {}) {
+static void GetHomeInitFile(FileSpec &init_file, llvm::StringRef suffix = {}) {
std::string init_file_name = ".lldbinit";
if (!suffix.empty()) {
init_file_name.append("-");
init_file_name.append(suffix.str());
}
- FileSystem::Instance().GetHomeDirectory(init_file);
- llvm::sys::path::append(init_file, init_file_name);
-
- FileSystem::Instance().Resolve(init_file);
+ init_file =
+ HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name);
}
-static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file,
- LanguageType language) {
+static void GetHomeREPLInitFile(FileSpec &init_file, LanguageType language) {
if (language == eLanguageTypeUnknown) {
LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs();
if (auto main_repl_language = repl_languages.GetSingularLanguage())
@@ -2531,9 +2528,9 @@ static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file,
llvm::Twine(Language::GetNameForLanguageType(language)) +
llvm::Twine("-repl"))
.str();
- FileSystem::Instance().GetHomeDirectory(init_file);
- llvm::sys::path::append(init_file, init_file_name);
- FileSystem::Instance().Resolve(init_file);
+
+ init_file =
+ HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name);
}
static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) {
@@ -2588,10 +2585,10 @@ void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {
SourceInitFile(FileSpec(init_file.str()), result);
break;
case eLoadCWDlldbinitWarn: {
- llvm::SmallString<128> home_init_file;
+ FileSpec home_init_file;
GetHomeInitFile(home_init_file);
if (llvm::sys::path::parent_path(init_file) ==
- llvm::sys::path::parent_path(home_init_file)) {
+ llvm::sys::path::parent_path(home_init_file.GetPath())) {
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
result.AppendError(InitFileWarning);
@@ -2611,24 +2608,24 @@ void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result,
return;
}
- llvm::SmallString<128> init_file;
+ FileSpec init_file;
if (is_repl)
GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage());
- if (init_file.empty())
+ if (init_file.GetPath().empty())
GetHomeInitFile(init_file);
if (!m_skip_app_init_files) {
llvm::StringRef program_name =
HostInfo::GetProgramFileSpec().GetFilename().GetStringRef();
- llvm::SmallString<128> program_init_file;
+ FileSpec program_init_file;
GetHomeInitFile(program_init_file, program_name);
if (FileSystem::Instance().Exists(program_init_file))
init_file = program_init_file;
}
- SourceInitFile(FileSpec(init_file.str()), result);
+ SourceInitFile(init_file, result);
}
void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) {
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 129646ebddb9..22eeceb2ef7e 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -217,7 +217,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
if (process == nullptr) {
// A process that is not running is considered paused.
if (GetFlags().Test(eCommandProcessMustBeLaunched)) {
- result.AppendError("Process must exist.");
+ result.AppendError("process must exist");
return false;
}
} else {
@@ -236,7 +236,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
case eStateExited:
case eStateUnloaded:
if (GetFlags().Test(eCommandProcessMustBeLaunched)) {
- result.AppendError("Process must be launched.");
+ result.AppendError("process must be launched");
return false;
}
break;
@@ -255,7 +255,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
if (GetFlags().Test(eCommandProcessMustBeTraced)) {
Target *target = m_exe_ctx.GetTargetPtr();
if (target && !target->GetTrace()) {
- result.AppendError("Process is not being traced.");
+ result.AppendError("process is not being traced");
return false;
}
}
@@ -359,7 +359,8 @@ bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word,
StreamString usage_help;
GetOptions()->GenerateOptionUsage(
usage_help, *this,
- GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
+ GetCommandInterpreter().GetDebugger().GetUseColor());
if (!usage_help.Empty()) {
llvm::StringRef usage_text = usage_help.GetString();
if (usage_text.contains_insensitive(search_word))
@@ -672,7 +673,8 @@ void CommandObject::GenerateHelpText(Stream &output_strm) {
if (options != nullptr) {
options->GenerateOptionUsage(
output_strm, *this,
- GetCommandInterpreter().GetDebugger().GetTerminalWidth());
+ GetCommandInterpreter().GetDebugger().GetTerminalWidth(),
+ GetCommandInterpreter().GetDebugger().GetUseColor());
}
llvm::StringRef long_help = GetHelpLong();
if (!long_help.empty()) {
diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp
index 4cf68db46615..cae617813d2f 100644
--- a/lldb/source/Interpreter/Options.cpp
+++ b/lldb/source/Interpreter/Options.cpp
@@ -19,6 +19,7 @@
#include "lldb/Interpreter/CommandObject.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Target/Target.h"
+#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/StreamString.h"
#include "llvm/ADT/STLExtras.h"
@@ -261,7 +262,8 @@ Option *Options::GetLongOptions() {
void Options::OutputFormattedUsageText(Stream &strm,
const OptionDefinition &option_def,
- uint32_t output_max_columns) {
+ uint32_t output_max_columns,
+ bool use_color) {
std::string actual_text;
if (option_def.validator) {
const char *condition = option_def.validator->ShortConditionString();
@@ -271,14 +273,16 @@ void Options::OutputFormattedUsageText(Stream &strm,
actual_text.append("] ");
}
}
- actual_text.append(option_def.usage_text);
+ actual_text.append(
+ ansi::FormatAnsiTerminalCodes(option_def.usage_text, use_color));
+ const size_t visible_length = ansi::ColumnWidth(actual_text);
// Will it all fit on one line?
- if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) <
+ if (static_cast<uint32_t>(visible_length + strm.GetIndentLevel()) <
output_max_columns) {
// Output it as a single line.
- strm.Indent(actual_text);
+ strm.Indent(ansi::FormatAnsiTerminalCodes(actual_text, use_color));
strm.EOL();
} else {
// We need to break it up into multiple lines.
@@ -286,7 +290,7 @@ void Options::OutputFormattedUsageText(Stream &strm,
int text_width = output_max_columns - strm.GetIndentLevel() - 1;
int start = 0;
int end = start;
- int final_end = actual_text.length();
+ int final_end = visible_length;
int sub_len;
while (end < final_end) {
@@ -312,7 +316,8 @@ void Options::OutputFormattedUsageText(Stream &strm,
strm.Indent();
assert(start < final_end);
assert(start + sub_len <= final_end);
- strm.Write(actual_text.c_str() + start, sub_len);
+ strm.PutCString(ansi::FormatAnsiTerminalCodes(
+ llvm::StringRef(actual_text.c_str() + start, sub_len), use_color));
start = end + 1;
}
strm.EOL();
@@ -385,7 +390,7 @@ static bool PrintOption(const OptionDefinition &opt_def,
}
void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd,
- uint32_t screen_width) {
+ uint32_t screen_width, bool use_color) {
auto opt_defs = GetDefinitions();
const uint32_t save_indent_level = strm.GetIndentLevel();
llvm::StringRef name = cmd.GetCommandName();
@@ -527,7 +532,7 @@ void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd,
strm.IndentMore(5);
if (opt_def.usage_text)
- OutputFormattedUsageText(strm, opt_def, screen_width);
+ OutputFormattedUsageText(strm, opt_def, screen_width, use_color);
if (!opt_def.enum_values.empty()) {
strm.Indent();
strm.Printf("Values: ");
@@ -628,6 +633,7 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
auto opt_defs = GetDefinitions();
llvm::StringRef cur_opt_str = request.GetCursorArgumentPrefix();
+ const bool use_color = interpreter.GetDebugger().GetUseColor();
for (size_t i = 0; i < opt_element_vector.size(); i++) {
size_t opt_pos = static_cast<size_t>(opt_element_vector[i].opt_pos);
@@ -647,7 +653,8 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
if (!def.short_option)
continue;
opt_str[1] = def.short_option;
- request.AddCompletion(opt_str, def.usage_text);
+ request.AddCompletion(opt_str, ansi::FormatAnsiTerminalCodes(
+ def.usage_text, use_color));
}
return true;
@@ -659,7 +666,8 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
full_name.erase(full_name.begin() + 2, full_name.end());
full_name.append(def.long_option);
- request.AddCompletion(full_name, def.usage_text);
+ request.AddCompletion(full_name, ansi::FormatAnsiTerminalCodes(
+ def.usage_text, use_color));
}
return true;
} else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) {
@@ -670,7 +678,9 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
const OptionDefinition &opt = opt_defs[opt_defs_index];
llvm::StringRef long_option = opt.long_option;
if (cur_opt_str.starts_with("--") && cur_opt_str != long_option) {
- request.AddCompletion("--" + long_option.str(), opt.usage_text);
+ request.AddCompletion(
+ "--" + long_option.str(),
+ ansi::FormatAnsiTerminalCodes(opt.usage_text, use_color));
return true;
} else
request.AddCompletion(request.GetCursorArgumentPrefix());
@@ -686,7 +696,9 @@ bool Options::HandleOptionCompletion(CompletionRequest &request,
for (auto &def : opt_defs) {
llvm::StringRef long_option(def.long_option);
if (long_option.starts_with(cur_opt_str))
- request.AddCompletion("--" + long_option.str(), def.usage_text);
+ request.AddCompletion(
+ "--" + long_option.str(),
+ ansi::FormatAnsiTerminalCodes(def.usage_text, use_color));
}
}
return true;
@@ -1282,7 +1294,7 @@ llvm::Expected<Args> Options::Parse(const Args &args,
Status error;
Option *long_options = GetLongOptions();
if (long_options == nullptr) {
- return llvm::createStringError("Invalid long options.");
+ return llvm::createStringError("invalid long options");
}
std::string short_options = BuildShortOptions(long_options);
diff --git a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp
index 2bcb2c0de97a..bb0c4ba3f1b5 100644
--- a/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp
+++ b/lldb/source/Plugins/ABI/ARM/ABISysV_arm.cpp
@@ -1921,6 +1921,13 @@ UnwindPlanSP ABISysV_arm::CreateFunctionEntryUnwindPlan() {
UnwindPlanSP ABISysV_arm::CreateDefaultUnwindPlan() {
// TODO: Handle thumb
+ // If we had a Target argument, could at least check
+ // target.GetArchitecture().GetTriple().isArmMClass()
+ // which is always thumb.
+ // To handle thumb properly, we'd need to fetch the current
+ // CPSR state at unwind time to tell if the processor is
+ // in thumb mode in this stack frame. There's no way to
+ // express something like that in an UnwindPlan today.
uint32_t fp_reg_num = dwarf_r11;
uint32_t pc_reg_num = dwarf_pc;
diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
index 81c72122cb7e..721c4bcfe634 100644
--- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
+++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.cpp
@@ -9,10 +9,18 @@
#include "Plugins/Architecture/Arm/ArchitectureArm.h"
#include "Plugins/Process/Utility/ARMDefines.h"
#include "Plugins/Process/Utility/InstructionUtils.h"
+#include "Utility/ARM_DWARF_Registers.h"
#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/UnwindPlan.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterNumber.h"
#include "lldb/Target/Thread.h"
+#include "lldb/Target/UnwindLLDB.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
using namespace lldb_private;
using namespace lldb;
@@ -150,3 +158,181 @@ addr_t ArchitectureArm::GetOpcodeLoadAddress(addr_t opcode_addr,
}
return opcode_addr & ~(1ull);
}
+
+// The ARM M-Profile Armv7-M Architecture Reference Manual,
+// subsection "B1.5 Armv7-M exception model", see the parts
+// describing "Exception entry behavior" and "Exception
+// return behavior".
+// When an exception happens on this processor, certain registers are
+// saved below the stack pointer, the stack pointer is decremented,
+// a special value is put in the link register to indicate the
+// exception has been taken, and an exception handler function
+// is invoked.
+//
+// Detect that special value in $lr, and if present, add
+// unwind rules for the registers that were saved above this
+// stack frame's CFA. Overwrite any register locations that
+// the current_unwindplan has for these registers; they are
+// not correct when we're invoked this way.
+UnwindPlanSP ArchitectureArm::GetArchitectureUnwindPlan(
+ Thread &thread, RegisterContextUnwind *regctx,
+ std::shared_ptr<const UnwindPlan> current_unwindplan) {
+
+ ProcessSP process_sp = thread.GetProcess();
+ if (!process_sp)
+ return {};
+
+ const ArchSpec arch = process_sp->GetTarget().GetArchitecture();
+ if (!arch.GetTriple().isArmMClass() || arch.GetAddressByteSize() != 4)
+ return {};
+
+ // Get the caller's LR value from regctx (the LR value
+ // at function entry to this function).
+ RegisterNumber ra_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_RA);
+ uint32_t ra_regnum_lldb = ra_regnum.GetAsKind(eRegisterKindLLDB);
+
+ if (ra_regnum_lldb == LLDB_INVALID_REGNUM)
+ return {};
+
+ UnwindLLDB::ConcreteRegisterLocation regloc = {};
+ bool got_concrete_location = false;
+ if (regctx->SavedLocationForRegister(ra_regnum_lldb, regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound) {
+ got_concrete_location = true;
+ } else {
+ RegisterNumber pc_regnum(thread, eRegisterKindGeneric,
+ LLDB_REGNUM_GENERIC_PC);
+ uint32_t pc_regnum_lldb = pc_regnum.GetAsKind(eRegisterKindLLDB);
+ if (regctx->SavedLocationForRegister(pc_regnum_lldb, regloc) ==
+ UnwindLLDB::RegisterSearchResult::eRegisterFound)
+ got_concrete_location = true;
+ }
+
+ if (!got_concrete_location)
+ return {};
+
+ addr_t callers_return_address = LLDB_INVALID_ADDRESS;
+ const RegisterInfo *reg_info = regctx->GetRegisterInfoAtIndex(ra_regnum_lldb);
+ if (reg_info) {
+ RegisterValue reg_value;
+ if (regctx->ReadRegisterValueFromRegisterLocation(regloc, reg_info,
+ reg_value)) {
+ callers_return_address = reg_value.GetAsUInt32();
+ }
+ }
+
+ if (callers_return_address == LLDB_INVALID_ADDRESS)
+ return {};
+
+ // ARMv7-M ARM says that the LR will be set to
+ // one of these values when an exception has taken
+ // place:
+ // if HaveFPExt() then
+ // if CurrentMode==Mode_Handler then
+ // LR = Ones(27):NOT(CONTROL.FPCA):'0001';
+ // else
+ // LR = Ones(27):NOT(CONTROL.FPCA):'1':CONTROL.SPSEL:'01';
+ // else
+ // if CurrentMode==Mode_Handler then
+ // LR = Ones(28):'0001';
+ // else
+ // LR = Ones(29):CONTROL.SPSEL:'01';
+
+ // Top 27 bits are set for an exception return.
+ const uint32_t exception_return = -1U & ~0b11111U;
+ // Bit4 is 1 if only GPRs were saved.
+ const uint32_t gprs_only = 0b10000;
+ // Bit<1:0> are '01'.
+ const uint32_t lowbits = 0b01;
+
+ if ((callers_return_address & exception_return) != exception_return)
+ return {};
+ if ((callers_return_address & lowbits) != lowbits)
+ return {};
+
+ const bool fp_regs_saved = !(callers_return_address & gprs_only);
+
+ const RegisterKind plan_regkind = current_unwindplan->GetRegisterKind();
+ UnwindPlanSP new_plan = std::make_shared<UnwindPlan>(plan_regkind);
+ new_plan->SetSourceName("Arm Cortex-M exception return UnwindPlan");
+ new_plan->SetSourcedFromCompiler(eLazyBoolNo);
+ new_plan->SetUnwindPlanValidAtAllInstructions(eLazyBoolYes);
+ new_plan->SetUnwindPlanForSignalTrap(eLazyBoolYes);
+
+ int stored_regs_size = fp_regs_saved ? 0x68 : 0x20;
+
+ uint32_t gpr_regs[] = {dwarf_r0, dwarf_r1, dwarf_r2, dwarf_r3,
+ dwarf_r12, dwarf_lr, dwarf_pc, dwarf_cpsr};
+ const int gpr_reg_count = std::size(gpr_regs);
+ uint32_t fpr_regs[] = {dwarf_s0, dwarf_s1, dwarf_s2, dwarf_s3,
+ dwarf_s4, dwarf_s5, dwarf_s6, dwarf_s7,
+ dwarf_s8, dwarf_s9, dwarf_s10, dwarf_s11,
+ dwarf_s12, dwarf_s13, dwarf_s14, dwarf_s15};
+ const int fpr_reg_count = std::size(fpr_regs);
+
+ RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
+ std::vector<uint32_t> saved_regs;
+ for (int i = 0; i < gpr_reg_count; i++) {
+ uint32_t regno = gpr_regs[i];
+ reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, gpr_regs[i],
+ plan_regkind, regno);
+ saved_regs.push_back(regno);
+ }
+ if (fp_regs_saved) {
+ for (int i = 0; i < fpr_reg_count; i++) {
+ uint32_t regno = fpr_regs[i];
+ reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, fpr_regs[i],
+ plan_regkind, regno);
+ saved_regs.push_back(regno);
+ }
+ }
+
+ addr_t cfa;
+ if (!regctx->GetCFA(cfa))
+ return {};
+
+ // The CPSR value saved to stack is actually (from Armv7-M ARM)
+ // "XPSR<31:10>:frameptralign:XPSR<8:0>"
+ // Bit 9 indicates that the stack pointer was aligned (to
+ // an 8-byte alignment) when the exception happened, and we must
+ // account for that when restoring the original stack pointer value.
+ Status error;
+ uint32_t callers_xPSR =
+ process_sp->ReadUnsignedIntegerFromMemory(cfa + 0x1c, 4, 0, error);
+ const bool align_stack = callers_xPSR & (1U << 9);
+ uint32_t callers_sp = cfa + stored_regs_size;
+ if (align_stack)
+ callers_sp |= 4;
+
+ Log *log = GetLog(LLDBLog::Unwind);
+ LLDB_LOGF(log,
+ "ArchitectureArm::GetArchitectureUnwindPlan found caller return "
+ "addr of 0x%" PRIx64 ", for frame with CFA 0x%" PRIx64
+ ", fp_regs_saved %d, stored_regs_size 0x%x, align stack %d",
+ callers_return_address, cfa, fp_regs_saved, stored_regs_size,
+ align_stack);
+
+ uint32_t sp_regnum = dwarf_sp;
+ reg_ctx_sp->ConvertBetweenRegisterKinds(eRegisterKindDWARF, dwarf_sp,
+ plan_regkind, sp_regnum);
+
+ const int row_count = current_unwindplan->GetRowCount();
+ for (int i = 0; i < row_count; i++) {
+ UnwindPlan::Row row = *current_unwindplan->GetRowAtIndex(i);
+ uint32_t offset = 0;
+ const size_t saved_reg_count = saved_regs.size();
+ for (size_t j = 0; j < saved_reg_count; j++) {
+ // The locations could be set with
+ // SetRegisterLocationToIsConstant(regno, cfa+offset)
+ // expressing it in terms of CFA addr+offset - this UnwindPlan
+ // is only used once, with this specific CFA. I'm not sure
+ // which will be clearer for someone reading the unwind log.
+ row.SetRegisterLocationToAtCFAPlusOffset(saved_regs[j], offset, true);
+ offset += 4;
+ }
+ row.SetRegisterLocationToIsCFAPlusOffset(sp_regnum, callers_sp - cfa, true);
+ new_plan->AppendRow(row);
+ }
+ return new_plan;
+}
diff --git a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
index f579d6b62505..52277dc5dbae 100644
--- a/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
+++ b/lldb/source/Plugins/Architecture/Arm/ArchitectureArm.h
@@ -10,6 +10,7 @@
#define LLDB_SOURCE_PLUGINS_ARCHITECTURE_ARM_ARCHITECTUREARM_H
#include "lldb/Core/Architecture.h"
+#include "lldb/Target/Thread.h"
namespace lldb_private {
@@ -29,6 +30,10 @@ public:
lldb::addr_t GetOpcodeLoadAddress(lldb::addr_t load_addr,
AddressClass addr_class) const override;
+ lldb::UnwindPlanSP GetArchitectureUnwindPlan(
+ lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx,
+ std::shared_ptr<const UnwindPlan> current_unwindplan) override;
+
private:
static std::unique_ptr<Architecture> Create(const ArchSpec &arch);
ArchitectureArm() = default;
diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
index d92e16366f0a..66d0a50985be 100644
--- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
+++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp
@@ -559,7 +559,7 @@ public:
lldb::InstructionControlFlowKind
GetControlFlowKind(const lldb_private::ExecutionContext *exe_ctx) override {
DisassemblerScope disasm(*this, exe_ctx);
- if (disasm){
+ if (disasm) {
if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86)
return x86::GetControlFlowKind(/*is_64b=*/false, m_opcode);
else if (disasm->GetArchitecture().GetMachine() == llvm::Triple::x86_64)
@@ -1249,12 +1249,15 @@ private:
};
std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>
-DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple, const char *cpu,
+DisassemblerLLVMC::MCDisasmInstance::Create(const char *triple_name,
+ const char *cpu,
const char *features_str,
unsigned flavor,
DisassemblerLLVMC &owner) {
using Instance = std::unique_ptr<DisassemblerLLVMC::MCDisasmInstance>;
+ llvm::Triple triple(triple_name);
+
std::string Status;
const llvm::Target *curr_target =
llvm::TargetRegistry::lookupTarget(triple, Status);
@@ -1605,9 +1608,8 @@ DisassemblerLLVMC::DisassemblerLLVMC(const ArchSpec &arch,
// thumb instruction disassembler.
if (llvm_arch == llvm::Triple::arm) {
std::string thumb_triple(thumb_arch.GetTriple().getTriple());
- m_alternate_disasm_up =
- MCDisasmInstance::Create(thumb_triple.c_str(), "", features_str.c_str(),
- flavor, *this);
+ m_alternate_disasm_up = MCDisasmInstance::Create(
+ thumb_triple.c_str(), "", features_str.c_str(), flavor, *this);
if (!m_alternate_disasm_up)
m_disasm_up.reset();
diff --git a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
index 42ea5aacecb4..8b1c3c3f467f 100644
--- a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
+++ b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
@@ -45,7 +45,8 @@ protected:
lldb::addr_t GetLoadAddress(lldb::ModuleSP executable);
private:
- std::map<lldb::ModuleSP, lldb::addr_t> m_loaded_modules;
+ std::map<lldb::ModuleWP, lldb::addr_t, std::owner_less<lldb::ModuleWP>>
+ m_loaded_modules;
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 86ff010e760f..097a4661e0c9 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -536,23 +536,19 @@ static void SetupLangOpts(CompilerInstance &compiler,
lldb::StackFrameSP frame_sp = exe_scope.CalculateStackFrame();
lldb::ProcessSP process_sp = exe_scope.CalculateProcess();
- // Defaults to lldb::eLanguageTypeUnknown.
- lldb::LanguageType frame_lang = expr.Language().AsLanguageType();
-
- // Make sure the user hasn't provided a preferred execution language with
- // `expression --language X -- ...`
- if (frame_sp && frame_lang == lldb::eLanguageTypeUnknown)
- frame_lang = frame_sp->GetLanguage().AsLanguageType();
+ lldb::LanguageType language = expr.Language().AsLanguageType();
- if (process_sp && frame_lang != lldb::eLanguageTypeUnknown) {
- LLDB_LOGF(log, "Frame has language of type %s",
- lldb_private::Language::GetNameForLanguageType(frame_lang));
- }
+ if (process_sp)
+ LLDB_LOG(
+ log,
+ "Frame has language of type {0}\nPicked {1} for expression evaluation.",
+ lldb_private::Language::GetNameForLanguageType(
+ frame_sp ? frame_sp->GetLanguage().AsLanguageType()
+ : lldb::eLanguageTypeUnknown),
+ lldb_private::Language::GetNameForLanguageType(language));
- lldb::LanguageType language = expr.Language().AsLanguageType();
LangOptions &lang_opts = compiler.getLangOpts();
- // FIXME: should this switch on frame_lang?
switch (language) {
case lldb::eLanguageTypeC:
case lldb::eLanguageTypeC89:
diff --git a/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp b/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
index 926555380334..8bcb4e5535d2 100644
--- a/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
+++ b/lldb/source/Plugins/Instruction/MIPS/EmulateInstructionMIPS.cpp
@@ -147,17 +147,15 @@ EmulateInstructionMIPS::EmulateInstructionMIPS(
if (arch_flags & ArchSpec::eMIPSAse_dspr2)
features += "+dspr2,";
- m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ m_reg_info.reset(target->createMCRegInfo(triple));
assert(m_reg_info.get());
m_insn_info.reset(target->createMCInstrInfo());
assert(m_insn_info.get());
llvm::MCTargetOptions MCOptions;
- m_asm_info.reset(
- target->createMCAsmInfo(*m_reg_info, triple.getTriple(), MCOptions));
- m_subtype_info.reset(
- target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple, MCOptions));
+ m_subtype_info.reset(target->createMCSubtargetInfo(triple, cpu, features));
assert(m_asm_info.get() && m_subtype_info.get());
m_context = std::make_unique<llvm::MCContext>(
@@ -174,7 +172,7 @@ EmulateInstructionMIPS::EmulateInstructionMIPS(
features += "+micromips,";
m_alt_subtype_info.reset(
- target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ target->createMCSubtargetInfo(triple, cpu, features));
assert(m_alt_subtype_info.get());
m_alt_disasm.reset(
@@ -1005,11 +1003,10 @@ bool EmulateInstructionMIPS::SetInstruction(const Opcode &insn_opcode,
uint64_t next_inst_addr = (m_addr & (~1ull)) + current_inst_size;
Address next_addr(next_inst_addr);
- const size_t bytes_read =
- target->ReadMemory(next_addr, /* Address of next instruction */
- buf, sizeof(uint32_t), error,
- false, /* force_live_memory */
- &load_addr);
+ const size_t bytes_read = target->ReadMemory(
+ next_addr, /* Address of next instruction */
+ buf, sizeof(uint32_t), error, false, /* force_live_memory */
+ &load_addr);
if (bytes_read == 0)
return true;
diff --git a/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp b/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
index 780cc68fb75e..ad55d0ba97ad 100644
--- a/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
+++ b/lldb/source/Plugins/Instruction/MIPS64/EmulateInstructionMIPS64.cpp
@@ -151,17 +151,15 @@ EmulateInstructionMIPS64::EmulateInstructionMIPS64(
if (arch_flags & ArchSpec::eMIPSAse_micromips)
features += "+micromips,";
- m_reg_info.reset(target->createMCRegInfo(triple.getTriple()));
+ m_reg_info.reset(target->createMCRegInfo(triple));
assert(m_reg_info.get());
m_insn_info.reset(target->createMCInstrInfo());
assert(m_insn_info.get());
llvm::MCTargetOptions MCOptions;
- m_asm_info.reset(
- target->createMCAsmInfo(*m_reg_info, triple.getTriple(), MCOptions));
- m_subtype_info.reset(
- target->createMCSubtargetInfo(triple.getTriple(), cpu, features));
+ m_asm_info.reset(target->createMCAsmInfo(*m_reg_info, triple, MCOptions));
+ m_subtype_info.reset(target->createMCSubtargetInfo(triple, cpu, features));
assert(m_asm_info.get() && m_subtype_info.get());
m_context = std::make_unique<llvm::MCContext>(
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index b4207439f528..277de8f44482 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -604,126 +604,6 @@ bool CPlusPlusLanguage::ExtractContextAndIdentifier(
return false;
}
-namespace {
-class NodeAllocator {
- llvm::BumpPtrAllocator Alloc;
-
-public:
- void reset() { Alloc.Reset(); }
-
- template <typename T, typename... Args> T *makeNode(Args &&...args) {
- return new (Alloc.Allocate(sizeof(T), alignof(T)))
- T(std::forward<Args>(args)...);
- }
-
- void *allocateNodeArray(size_t sz) {
- return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
- alignof(llvm::itanium_demangle::Node *));
- }
-};
-
-template <typename Derived>
-class ManglingSubstitutor
- : public llvm::itanium_demangle::AbstractManglingParser<Derived,
- NodeAllocator> {
- using Base =
- llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>;
-
-public:
- ManglingSubstitutor() : Base(nullptr, nullptr) {}
-
- template <typename... Ts>
- ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) {
- this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...);
- return substituteImpl(Mangled);
- }
-
-protected:
- void reset(llvm::StringRef Mangled) {
- Base::reset(Mangled.begin(), Mangled.end());
- Written = Mangled.begin();
- Result.clear();
- Substituted = false;
- }
-
- ConstString substituteImpl(llvm::StringRef Mangled) {
- Log *log = GetLog(LLDBLog::Language);
- if (this->parse() == nullptr) {
- LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled);
- return ConstString();
- }
- if (!Substituted)
- return ConstString();
-
- // Append any trailing unmodified input.
- appendUnchangedInput();
- LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
- return ConstString(Result);
- }
-
- void trySubstitute(llvm::StringRef From, llvm::StringRef To) {
- if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From))
- return;
-
- // We found a match. Append unmodified input up to this point.
- appendUnchangedInput();
-
- // And then perform the replacement.
- Result += To;
- Written += From.size();
- Substituted = true;
- }
-
-private:
- /// Input character until which we have constructed the respective output
- /// already.
- const char *Written = "";
-
- llvm::SmallString<128> Result;
-
- /// Whether we have performed any substitutions.
- bool Substituted = false;
-
- const char *currentParserPos() const { return this->First; }
-
- void appendUnchangedInput() {
- Result +=
- llvm::StringRef(Written, std::distance(Written, currentParserPos()));
- Written = currentParserPos();
- }
-};
-
-/// Given a mangled function `Mangled`, replace all the primitive function type
-/// arguments of `Search` with type `Replace`.
-class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> {
- llvm::StringRef Search;
- llvm::StringRef Replace;
-
-public:
- void reset(llvm::StringRef Mangled, llvm::StringRef Search,
- llvm::StringRef Replace) {
- ManglingSubstitutor::reset(Mangled);
- this->Search = Search;
- this->Replace = Replace;
- }
-
- llvm::itanium_demangle::Node *parseType() {
- trySubstitute(Search, Replace);
- return ManglingSubstitutor::parseType();
- }
-};
-
-class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> {
-public:
- llvm::itanium_demangle::Node *
- parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) {
- trySubstitute("C1", "C2");
- trySubstitute("D1", "D2");
- return ManglingSubstitutor::parseCtorDtorName(SoFar, State);
- }
-};
-} // namespace
-
std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings(
const ConstString mangled_name) const {
std::vector<ConstString> alternates;
@@ -751,29 +631,49 @@ std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings(
alternates.push_back(ConstString(fixed_scratch));
}
- TypeSubstitutor TS;
+ auto *log = GetLog(LLDBLog::Language);
+
// `char` is implementation defined as either `signed` or `unsigned`. As a
// result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed
// char, 'h'-unsigned char. If we're looking for symbols with a signed char
// parameter, try finding matches which have the general case 'c'.
- if (ConstString char_fixup =
- TS.substitute(mangled_name.GetStringRef(), "a", "c"))
- alternates.push_back(char_fixup);
+ if (auto char_fixup_or_err =
+ SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "a", "c")) {
+ // LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
+ if (*char_fixup_or_err)
+ alternates.push_back(*char_fixup_or_err);
+ } else
+ LLDB_LOG_ERROR(log, char_fixup_or_err.takeError(),
+ "Failed to substitute 'char' type mangling: {0}");
// long long parameter mangling 'x', may actually just be a long 'l' argument
- if (ConstString long_fixup =
- TS.substitute(mangled_name.GetStringRef(), "x", "l"))
- alternates.push_back(long_fixup);
+ if (auto long_fixup_or_err =
+ SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "x", "l")) {
+ if (*long_fixup_or_err)
+ alternates.push_back(*long_fixup_or_err);
+ } else
+ LLDB_LOG_ERROR(log, long_fixup_or_err.takeError(),
+ "Failed to substitute 'long long' type mangling: {0}");
// unsigned long long parameter mangling 'y', may actually just be unsigned
// long 'm' argument
- if (ConstString ulong_fixup =
- TS.substitute(mangled_name.GetStringRef(), "y", "m"))
- alternates.push_back(ulong_fixup);
-
- if (ConstString ctor_fixup =
- CtorDtorSubstitutor().substitute(mangled_name.GetStringRef()))
- alternates.push_back(ctor_fixup);
+ if (auto ulong_fixup_or_err =
+ SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "y", "m")) {
+ if (*ulong_fixup_or_err)
+ alternates.push_back(*ulong_fixup_or_err);
+ } else
+ LLDB_LOG_ERROR(
+ log, ulong_fixup_or_err.takeError(),
+ "Failed to substitute 'unsigned long long' type mangling: {0}");
+
+ if (auto ctor_fixup_or_err = SubstituteStructorAliases_ItaniumMangle(
+ mangled_name.GetStringRef())) {
+ if (*ctor_fixup_or_err) {
+ alternates.push_back(*ctor_fixup_or_err);
+ }
+ } else
+ LLDB_LOG_ERROR(log, ctor_fixup_or_err.takeError(),
+ "Failed to substitute structor alias manglings: {0}");
return alternates;
}
@@ -849,31 +749,27 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
lldb_private::formatters::LibcxxStringSummaryProviderASCII,
"std::string summary provider",
"^std::__[[:alnum:]]+::basic_string<char, "
- "std::__[[:alnum:]]+::char_traits<char>, "
- "std::__[[:alnum:]]+::allocator<char> >$",
+ "std::__[[:alnum:]]+::char_traits<char>,.*>$",
stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::LibcxxStringSummaryProviderASCII,
"std::string summary provider",
"^std::__[[:alnum:]]+::basic_string<unsigned char, "
- "std::__[[:alnum:]]+::char_traits<unsigned char>, "
- "std::__[[:alnum:]]+::allocator<unsigned char> >$",
+ "std::__[[:alnum:]]+::char_traits<unsigned char>,.*>$",
stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::LibcxxStringSummaryProviderUTF16,
"std::u16string summary provider",
"^std::__[[:alnum:]]+::basic_string<char16_t, "
- "std::__[[:alnum:]]+::char_traits<char16_t>, "
- "std::__[[:alnum:]]+::allocator<char16_t> >$",
+ "std::__[[:alnum:]]+::char_traits<char16_t>,.*>$",
stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
lldb_private::formatters::LibcxxStringSummaryProviderUTF32,
"std::u32string summary provider",
"^std::__[[:alnum:]]+::basic_string<char32_t, "
- "std::__[[:alnum:]]+::char_traits<char32_t>, "
- "std::__[[:alnum:]]+::allocator<char32_t> >$",
+ "std::__[[:alnum:]]+::char_traits<char32_t>,.*>$",
stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
@@ -884,8 +780,7 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
lldb_private::formatters::LibcxxWStringSummaryProvider,
"std::wstring summary provider",
"^std::__[[:alnum:]]+::basic_string<wchar_t, "
- "std::__[[:alnum:]]+::char_traits<wchar_t>, "
- "std::__[[:alnum:]]+::allocator<wchar_t> >$",
+ "std::__[[:alnum:]]+::char_traits<wchar_t>,.*>$",
stl_summary_flags, true);
AddCXXSummary(cpp_category_sp,
@@ -1401,24 +1296,16 @@ static void RegisterStdStringSummaryProvider(
category_sp->AddTypeSummary(makeSpecifier(string_ty), summary_sp);
- // std::basic_string<char>
category_sp->AddTypeSummary(
makeSpecifier(llvm::formatv("std::basic_string<{}>", char_ty).str()),
summary_sp);
- // std::basic_string<char,std::char_traits<char>,std::allocator<char> >
- category_sp->AddTypeSummary(
- makeSpecifier(llvm::formatv("std::basic_string<{0},std::char_traits<{0}>,"
- "std::allocator<{0}> >",
- char_ty)
- .str()),
- summary_sp);
- // std::basic_string<char, std::char_traits<char>, std::allocator<char> >
+
category_sp->AddTypeSummary(
- makeSpecifier(
- llvm::formatv("std::basic_string<{0}, std::char_traits<{0}>, "
- "std::allocator<{0}> >",
+ std::make_shared<lldb_private::TypeNameSpecifierImpl>(
+ llvm::formatv("^std::basic_string<{0}, ?std::char_traits<{0}>,.*>$",
char_ty)
- .str()),
+ .str(),
+ eFormatterMatchRegex),
summary_sp);
}
@@ -1463,20 +1350,17 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
cpp_category_sp->AddTypeSummary("std::__cxx11::string", eFormatterMatchExact,
string_summary_sp);
cpp_category_sp->AddTypeSummary(
- "std::__cxx11::basic_string<char, std::char_traits<char>, "
- "std::allocator<char> >",
- eFormatterMatchExact, string_summary_sp);
- cpp_category_sp->AddTypeSummary("std::__cxx11::basic_string<unsigned char, "
- "std::char_traits<unsigned char>, "
- "std::allocator<unsigned char> >",
- eFormatterMatchExact, string_summary_sp);
+ "^std::__cxx11::basic_string<char, std::char_traits<char>,.*>$",
+ eFormatterMatchRegex, string_summary_sp);
+ cpp_category_sp->AddTypeSummary("^std::__cxx11::basic_string<unsigned char, "
+ "std::char_traits<unsigned char>,.*>$",
+ eFormatterMatchRegex, string_summary_sp);
cpp_category_sp->AddTypeSummary("std::__cxx11::wstring", eFormatterMatchExact,
string_summary_sp);
cpp_category_sp->AddTypeSummary(
- "std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>, "
- "std::allocator<wchar_t> >",
- eFormatterMatchExact, string_summary_sp);
+ "^std::__cxx11::basic_string<wchar_t, std::char_traits<wchar_t>,.*>$",
+ eFormatterMatchRegex, string_summary_sp);
SyntheticChildren::Flags stl_synth_flags;
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
@@ -2442,6 +2326,160 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
}
}
+namespace {
+class NodeAllocator {
+ llvm::BumpPtrAllocator Alloc;
+
+public:
+ void reset() { Alloc.Reset(); }
+
+ template <typename T, typename... Args> T *makeNode(Args &&...args) {
+ return new (Alloc.Allocate(sizeof(T), alignof(T)))
+ T(std::forward<Args>(args)...);
+ }
+
+ void *allocateNodeArray(size_t sz) {
+ return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
+ alignof(llvm::itanium_demangle::Node *));
+ }
+};
+
+template <typename Derived>
+class ManglingSubstitutor
+ : public llvm::itanium_demangle::AbstractManglingParser<Derived,
+ NodeAllocator> {
+ using Base =
+ llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>;
+
+public:
+ ManglingSubstitutor() : Base(nullptr, nullptr) {}
+
+ template <typename... Ts>
+ llvm::Expected<ConstString> substitute(llvm::StringRef Mangled,
+ Ts &&...Vals) {
+ this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...);
+ return substituteImpl(Mangled);
+ }
+
+protected:
+ void reset(llvm::StringRef Mangled) {
+ Base::reset(Mangled.begin(), Mangled.end());
+ Written = Mangled.begin();
+ Result.clear();
+ Substituted = false;
+ }
+
+ llvm::Expected<ConstString> substituteImpl(llvm::StringRef Mangled) {
+ if (this->parse() == nullptr)
+ return llvm::createStringError(
+ llvm::formatv("Failed to substitute mangling in '{0}'", Mangled));
+
+ if (!Substituted)
+ return ConstString();
+
+ // Append any trailing unmodified input.
+ appendUnchangedInput();
+ return ConstString(Result);
+ }
+
+ void trySubstitute(llvm::StringRef From, llvm::StringRef To) {
+ if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From))
+ return;
+
+ // We found a match. Append unmodified input up to this point.
+ appendUnchangedInput();
+
+ // And then perform the replacement.
+ Result += To;
+ Written += From.size();
+ Substituted = true;
+ }
+
+private:
+ /// Input character until which we have constructed the respective output
+ /// already.
+ const char *Written = "";
+
+ llvm::SmallString<128> Result;
+
+ /// Whether we have performed any substitutions.
+ bool Substituted = false;
+
+ const char *currentParserPos() const { return this->First; }
+
+ void appendUnchangedInput() {
+ Result +=
+ llvm::StringRef(Written, std::distance(Written, currentParserPos()));
+ Written = currentParserPos();
+ }
+};
+
+/// Given a mangled function `Mangled`, replace all the primitive function type
+/// arguments of `Search` with type `Replace`.
+class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> {
+ llvm::StringRef Search;
+ llvm::StringRef Replace;
+
+public:
+ void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+ llvm::StringRef Replace) {
+ ManglingSubstitutor::reset(Mangled);
+ this->Search = Search;
+ this->Replace = Replace;
+ }
+
+ llvm::itanium_demangle::Node *parseType() {
+ trySubstitute(Search, Replace);
+ return ManglingSubstitutor::parseType();
+ }
+};
+
+class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> {
+ llvm::StringRef Search;
+ llvm::StringRef Replace;
+
+public:
+ void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+ llvm::StringRef Replace) {
+ ManglingSubstitutor::reset(Mangled);
+ this->Search = Search;
+ this->Replace = Replace;
+ }
+
+ void reset(llvm::StringRef Mangled) { ManglingSubstitutor::reset(Mangled); }
+
+ llvm::itanium_demangle::Node *
+ parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) {
+ if (!Search.empty() && !Replace.empty()) {
+ trySubstitute(Search, Replace);
+ } else {
+ trySubstitute("D1", "D2");
+ trySubstitute("C1", "C2");
+ }
+ return ManglingSubstitutor::parseCtorDtorName(SoFar, State);
+ }
+};
+} // namespace
+
+llvm::Expected<ConstString>
+CPlusPlusLanguage::SubstituteType_ItaniumMangle(llvm::StringRef mangled_name,
+ llvm::StringRef subst_from,
+ llvm::StringRef subst_to) {
+ return TypeSubstitutor().substitute(mangled_name, subst_from, subst_to);
+}
+
+llvm::Expected<ConstString> CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
+ llvm::StringRef mangled_name, llvm::StringRef subst_from,
+ llvm::StringRef subst_to) {
+ return CtorDtorSubstitutor().substitute(mangled_name, subst_from, subst_to);
+}
+
+llvm::Expected<ConstString>
+CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ llvm::StringRef mangled_name) {
+ return CtorDtorSubstitutor().substitute(mangled_name);
+}
+
#define LLDB_PROPERTIES_language_cplusplus
#include "LanguageCPlusPlusProperties.inc"
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 4a30299dd265..9a528ca7b03f 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -164,6 +164,76 @@ public:
ConstString FindBestAlternateFunctionMangledName(
const Mangled mangled, const SymbolContext &sym_ctx) const override;
+ /// Substitutes Itanium type encoding substrings given by \c subst_from
+ /// in \c mangled_name with \c subst_to.
+ ///
+ /// This function will only replace Itanium type encodings (i.e., <type>
+ /// productions in the Itanium ABI mangling grammar). However, no verifiction
+ /// is done on whether \c subst_from or \c subst_to is a valid type encoding.
+ ///
+ /// \param[in] mangled_name Mangled name to perform the substitutions in.
+ /// This function only supports Itanium ABI mangling.
+ ///
+ /// \param[in] subst_from The substring to substitute.
+ ///
+ /// \param[in] subst_to The substring to insert.
+ ///
+ /// \returns The mangled string with substitutions. If no substitutions
+ /// have been made, returns an empty \c ConstString (even if the string
+ /// already contained the substitutions). If an error occurred, this function
+ /// returns the error.
+ ///
+ static llvm::Expected<ConstString>
+ SubstituteType_ItaniumMangle(llvm::StringRef mangled_name,
+ llvm::StringRef subst_from,
+ llvm::StringRef subst_to);
+
+ /// Substitutes Itanium structor encoding substrings given by \c subst_from
+ /// in \c mangled_name with \c subst_to.
+ ///
+ /// This function will only replace Itanium structor encodings (i.e.,
+ /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar).
+ /// However, no verifiction is done on whether \c subst_from or \c subst_to is
+ /// a valid structor encoding.
+ ///
+ /// \param[in] mangled_name Mangled name to perform the substitutions in.
+ /// This function only supports Itanium ABI mangling.
+ ///
+ /// \param[in] subst_from The substring to substitute.
+ ///
+ /// \param[in] subst_to The substring to insert.
+ ///
+ /// \returns The mangled string with substitutions. If no substitutions
+ /// have been made, returns an empty \c ConstString (even if the string
+ /// already contained the substitutions). If an error occurred, this function
+ /// returns the error.
+ ///
+ static llvm::Expected<ConstString>
+ SubstituteStructor_ItaniumMangle(llvm::StringRef mangled_name,
+ llvm::StringRef subst_from,
+ llvm::StringRef subst_to);
+
+ /// Tries replacing Itanium structor encoding substrings in \c mangled_name
+ /// with potential aliases.j
+ ///
+ /// This function will only replace Itanium structor encodings (i.e.,
+ /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar).
+ ///
+ /// E.g., on some platforms, the C1/D1 variants are aliased to the C2/D2
+ /// variants. This function will try to replace occurrences of C1/D1 with
+ /// C2/D2.
+ ///
+ /// \param[in] mangled_name Mangled name to perform the substitutions in.
+ /// This function only supports Itanium ABI mangling.
+ ///
+ /// \returns The mangled string with substitutions. If no substitutions
+ /// have been made, returns an empty \c ConstString (even if the string
+ /// already contained the substitutions). If an error occurred, this function
+ /// returns the error.
+ ///
+ static llvm::Expected<ConstString>
+ SubstituteStructorAliases_ItaniumMangle(llvm::StringRef mangled_name);
+
llvm::StringRef GetInstanceVariableName() override { return "this"; }
FormatEntity::Entry GetFunctionNameFormat() const override;
diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
index ea1edbfd3ac9..5289027fbd8a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp
@@ -339,11 +339,18 @@ lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() {
if (err.Fail() || !backend_addr)
return lldb::ChildCacheState::eRefetch;
- ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
+ auto list_base_sp = m_backend.GetChildAtIndex(0);
+ if (!list_base_sp)
+ return lldb::ChildCacheState::eRefetch;
+
+ // Anonymous strucutre index is in base class at index 0.
+ auto [impl_sp, is_compressed_pair] =
+ GetValueOrOldCompressedPair(*list_base_sp, /*anon_struct_idx=*/0,
+ "__before_begin_", "__before_begin_");
if (!impl_sp)
return ChildCacheState::eRefetch;
- if (isOldCompressedPairLayout(*impl_sp))
+ if (is_compressed_pair)
impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
if (!impl_sp)
@@ -366,17 +373,10 @@ llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() {
if (!m_head || !m_tail || m_node_address == 0)
return 0;
- ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_"));
- if (!size_node_sp) {
- size_node_sp = m_backend.GetChildMemberWithName(
- "__size_alloc_"); // pre-compressed_pair rework
-
- if (!isOldCompressedPairLayout(*size_node_sp))
- return llvm::createStringError("Unexpected std::list layout: expected "
- "old __compressed_pair layout.");
-
+ auto [size_node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ m_backend, /*anon_struct_idx=*/1, "__size_", "__size_alloc_");
+ if (is_compressed_pair)
size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
- }
if (size_node_sp)
m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index a7874047942c..6053d042b29b 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -49,11 +49,6 @@ static void consumeInlineNamespace(llvm::StringRef &name) {
}
}
-bool lldb_private::formatters::isOldCompressedPairLayout(
- ValueObject &pair_obj) {
- return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
-}
-
bool lldb_private::formatters::isStdTemplate(ConstString type_name,
llvm::StringRef type) {
llvm::StringRef name = type_name.GetStringRef();
@@ -105,6 +100,44 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
return value;
}
+std::pair<lldb::ValueObjectSP, bool>
+lldb_private::formatters::GetValueOrOldCompressedPair(
+ ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name,
+ llvm::StringRef compressed_pair_name) {
+ auto is_old_compressed_pair = [](ValueObject &pair_obj) -> bool {
+ return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
+ };
+
+ // Try searching the child member in an anonymous structure first.
+ if (auto unwrapped = obj.GetChildAtIndex(anon_struct_idx)) {
+ ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
+ if (node_sp)
+ return {node_sp, is_old_compressed_pair(*node_sp)};
+ }
+
+ // Older versions of libc++ don't wrap the children in anonymous structures.
+ // Try that instead.
+ ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name));
+ if (node_sp)
+ return {node_sp, is_old_compressed_pair(*node_sp)};
+
+ // Try the even older __compressed_pair layout.
+
+ assert(!compressed_pair_name.empty());
+
+ node_sp = obj.GetChildMemberWithName(compressed_pair_name);
+
+ // Unrecognized layout (possibly older than LLDB supports).
+ if (!node_sp)
+ return {nullptr, false};
+
+ // Expected old compressed_pair layout, but got something else.
+ if (!is_old_compressed_pair(*node_sp))
+ return {nullptr, false};
+
+ return {node_sp, true};
+}
+
bool lldb_private::formatters::LibcxxFunctionSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
@@ -205,11 +238,12 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
if (!valobj_sp)
return false;
- ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
+ auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_");
if (!ptr_sp)
return false;
- if (isOldCompressedPairLayout(*ptr_sp))
+ if (is_compressed_pair)
ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
if (!ptr_sp)
@@ -379,13 +413,14 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
if (!valobj_sp)
return lldb::ChildCacheState::eRefetch;
- ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_"));
+ auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_");
if (!ptr_sp)
return lldb::ChildCacheState::eRefetch;
// Retrieve the actual pointer and the deleter, and clone them to give them
// user-friendly names.
- if (isOldCompressedPairLayout(*ptr_sp)) {
+ if (is_compressed_pair) {
if (ValueObjectSP value_pointer_sp =
GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
@@ -424,17 +459,15 @@ enum class StringLayout { CSD, DSC };
}
static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
- if (auto rep_sp = valobj.GetChildMemberWithName("__rep_"))
- return rep_sp;
-
- ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
- if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
+ auto [valobj_r_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ valobj, /*anon_struct_idx=*/0, "__rep_", "__r_");
+ if (!valobj_r_sp)
return nullptr;
- if (!isOldCompressedPairLayout(*valobj_r_sp))
- return nullptr;
+ if (is_compressed_pair)
+ return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
- return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
+ return valobj_r_sp;
}
/// Determine the size in bytes of \p valobj (a libc++ std::string object) and
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index d88a6ecb1fa8..819f8a985f9b 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -25,7 +25,22 @@ GetChildMemberWithName(ValueObject &obj,
lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);
-bool isOldCompressedPairLayout(ValueObject &pair_obj);
+
+/// Returns the ValueObjectSP of the child of \c obj. If \c obj has no
+/// child named \c child_name, returns the __compressed_pair child instead
+/// with \c compressed_pair_name, if one exists.
+///
+/// Latest libc++ wrap the compressed children in an anonymous structure.
+/// The \c anon_struct_idx indicates the location of this struct.
+///
+/// The returned boolean is \c true if the returned child was has an old-style
+/// libc++ __compressed_pair layout.
+///
+/// If no child was found returns a nullptr.
+std::pair<lldb::ValueObjectSP, bool>
+GetValueOrOldCompressedPair(ValueObject &obj, size_t anon_struct_idx,
+ llvm::StringRef child_name,
+ llvm::StringRef compressed_pair_name);
bool isStdTemplate(ConstString type_name, llvm::StringRef type);
bool LibcxxStringSummaryProviderASCII(
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index 41441dfbc718..85766966f155 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -200,7 +200,8 @@ public:
llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override;
private:
- llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();
+ llvm::Expected<uint32_t>
+ CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair);
/// Returns the ValueObject for the __tree_node type that
/// holds the key/value pair of the node at index \ref idx.
@@ -254,16 +255,8 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
llvm::Expected<uint32_t>
lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
- CalculateNumChildrenForOldCompressedPairLayout() {
- ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
- if (!node_sp)
- return 0;
-
- if (!isOldCompressedPairLayout(*node_sp))
- return llvm::createStringError("Unexpected std::map layout: expected "
- "old __compressed_pair layout.");
-
- node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
+ CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair) {
+ auto node_sp = GetFirstValueOfLibCXXCompressedPair(pair);
if (!node_sp)
return 0;
@@ -281,12 +274,16 @@ llvm::Expected<uint32_t> lldb_private::formatters::
if (m_tree == nullptr)
return 0;
- if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
- m_count = node_sp->GetValueAsUnsigned(0);
- return m_count;
- }
+ auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ *m_tree, /*anon_struct_idx=*/2, "__size_", "__pair3_");
+ if (!size_sp)
+ return llvm::createStringError("Unexpected std::map layout");
- return CalculateNumChildrenForOldCompressedPairLayout();
+ if (is_compressed_pair)
+ return CalculateNumChildrenForOldCompressedPairLayout(*size_sp);
+
+ m_count = size_sp->GetValueAsUnsigned(0);
+ return m_count;
}
ValueObjectSP
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 501fd0945b82..f88a5319068a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -130,22 +130,17 @@ CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
GetNodeType() {
- auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"});
-
- if (!node_sp) {
- auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
- if (!p1_sp)
- return {};
+ auto table_sp = m_backend.GetChildMemberWithName("__table_");
+ if (!table_sp)
+ return {};
- if (!isOldCompressedPairLayout(*p1_sp))
- return {};
+ auto [node_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ *table_sp, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
+ if (is_compressed_pair)
+ node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
- node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
- if (!node_sp)
- return {};
- }
-
- assert(node_sp);
+ if (!node_sp)
+ return {};
return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType();
}
@@ -223,19 +218,15 @@ lldb::ValueObjectSP lldb_private::formatters::
llvm::Expected<size_t>
lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
CalculateNumChildrenImpl(ValueObject &table) {
- if (auto size_sp = table.GetChildMemberWithName("__size_"))
+ auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ table, /*anon_struct_idx=*/2, "__size_", "__p2_");
+ if (!is_compressed_pair && size_sp)
return size_sp->GetValueAsUnsigned(0);
- ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_");
- if (!p2_sp)
- return llvm::createStringError(
- "Unexpected std::unordered_map layout: __p2_ member not found.");
+ if (!is_compressed_pair)
+ return llvm::createStringError("Unsupported std::unordered_map layout.");
- if (!isOldCompressedPairLayout(*p2_sp))
- return llvm::createStringError("Unexpected std::unordered_map layout: old "
- "__compressed_pair layout not found.");
-
- ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);
+ ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp);
if (!num_elements_sp)
return llvm::createStringError(
@@ -246,19 +237,13 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
}
static ValueObjectSP GetTreePointer(ValueObject &table) {
- ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_");
- if (!tree_sp) {
- ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_");
- if (!p1_sp)
- return nullptr;
-
- if (!isOldCompressedPairLayout(*p1_sp))
- return nullptr;
-
- tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
- if (!tree_sp)
- return nullptr;
- }
+ auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ table, /*anon_struct_idx=*/1, "__first_node_", "__p1_");
+ if (is_compressed_pair)
+ tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp);
+
+ if (!tree_sp)
+ return nullptr;
return tree_sp->GetChildMemberWithName("__next_");
}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
index 4bcdf01c221a..202cebf9bf85 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -84,7 +84,7 @@ llvm::Expected<uint32_t> lldb_private::formatters::
LibcxxStdVectorSyntheticFrontEnd::CalculateNumChildren() {
if (!m_start || !m_finish)
return llvm::createStringError(
- "Failed to determine start/end of vector data.");
+ "failed to determine start/end of vector data");
uint64_t start_val = m_start->GetValueAsUnsigned(0);
uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
@@ -94,18 +94,18 @@ llvm::Expected<uint32_t> lldb_private::formatters::
return 0;
if (start_val == 0)
- return llvm::createStringError("Invalid value for start of vector.");
+ return llvm::createStringError("invalid value for start of vector");
if (finish_val == 0)
- return llvm::createStringError("Invalid value for end of vector.");
+ return llvm::createStringError("invalid value for end of vector");
if (start_val > finish_val)
return llvm::createStringError(
- "Start of vector data begins after end pointer.");
+ "start of vector data begins after end pointer");
size_t num_children = (finish_val - start_val);
if (num_children % m_element_size)
- return llvm::createStringError("Size not multiple of element size.");
+ return llvm::createStringError("size not multiple of element size");
return num_children / m_element_size;
}
@@ -126,17 +126,15 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
}
static ValueObjectSP GetDataPointer(ValueObject &root) {
- if (auto cap_sp = root.GetChildMemberWithName("__cap_"))
- return cap_sp;
-
- ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_");
+ auto [cap_sp, is_compressed_pair] = GetValueOrOldCompressedPair(
+ root, /*anon_struct_idx=*/2, "__cap_", "__end_cap_");
if (!cap_sp)
return nullptr;
- if (!isOldCompressedPairLayout(*cap_sp))
- return nullptr;
+ if (is_compressed_pair)
+ return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
- return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
+ return cap_sp;
}
lldb::ChildCacheState
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
index cfc98d27f56d..99e73ca46fa2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlVector.cpp
@@ -70,7 +70,7 @@ llvm::Expected<uint32_t> lldb_private::formatters::
MsvcStlVectorSyntheticFrontEnd::CalculateNumChildren() {
if (!m_start || !m_finish)
return llvm::createStringError(
- "Failed to determine start/end of vector data.");
+ "failed to determine start/end of vector data");
uint64_t start_val = m_start->GetValueAsUnsigned(0);
uint64_t finish_val = m_finish->GetValueAsUnsigned(0);
@@ -80,18 +80,18 @@ llvm::Expected<uint32_t> lldb_private::formatters::
return 0;
if (start_val == 0)
- return llvm::createStringError("Invalid value for start of vector.");
+ return llvm::createStringError("invalid value for start of vector");
if (finish_val == 0)
- return llvm::createStringError("Invalid value for end of vector.");
+ return llvm::createStringError("invalid value for end of vector");
if (start_val > finish_val)
return llvm::createStringError(
- "Start of vector data begins after end pointer.");
+ "start of vector data begins after end pointer");
size_t num_children = (finish_val - start_val);
if (num_children % m_element_size)
- return llvm::createStringError("Size not multiple of element size.");
+ return llvm::createStringError("size not multiple of element size");
return num_children / m_element_size;
}
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358de6a28..931baf5927a0 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -2037,6 +2037,19 @@ static char FindArmAarch64MappingSymbol(const char *symbol_name) {
return '\0';
}
+static char FindRISCVMappingSymbol(const char *symbol_name) {
+ if (!symbol_name)
+ return '\0';
+
+ if (strcmp(symbol_name, "$d") == 0) {
+ return 'd';
+ }
+ if (strcmp(symbol_name, "$x") == 0) {
+ return 'x';
+ }
+ return '\0';
+}
+
#define STO_MIPS_ISA (3 << 6)
#define STO_MICROMIPS (2 << 6)
#define IS_MICROMIPS(ST_OTHER) (((ST_OTHER)&STO_MIPS_ISA) == STO_MICROMIPS)
@@ -2102,6 +2115,12 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
if (!symbol_name)
symbol_name = "";
+ // Skip local symbols starting with ".L" because these are compiler
+ // generated local labels used for internal purposes (e.g. debugging,
+ // optimization) and are not relevant for symbol resolution or external
+ // linkage.
+ if (llvm::StringRef(symbol_name).starts_with(".L"))
+ continue;
// No need to add non-section symbols that have no names
if (symbol.getType() != STT_SECTION &&
(symbol_name == nullptr || symbol_name[0] == '\0'))
@@ -2190,7 +2209,6 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
int64_t symbol_value_offset = 0;
uint32_t additional_flags = 0;
-
if (arch.IsValid()) {
if (arch.GetMachine() == llvm::Triple::arm) {
if (symbol.getBinding() == STB_LOCAL) {
@@ -2235,6 +2253,27 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id,
if (mapping_symbol)
continue;
}
+ } else if (arch.GetTriple().isRISCV()) {
+ if (symbol.getBinding() == STB_LOCAL) {
+ char mapping_symbol = FindRISCVMappingSymbol(symbol_name);
+ if (symbol_type == eSymbolTypeCode) {
+ // Only handle $d and $x mapping symbols.
+ // Other mapping symbols are ignored as they don't affect address
+ // classification.
+ switch (mapping_symbol) {
+ case 'x':
+ // $x - marks a RISCV instruction sequence
+ address_class_map[symbol.st_value] = AddressClass::eCode;
+ break;
+ case 'd':
+ // $d - marks a RISCV data item sequence
+ address_class_map[symbol.st_value] = AddressClass::eData;
+ break;
+ }
+ }
+ if (mapping_symbol)
+ continue;
+ }
}
if (arch.GetMachine() == llvm::Triple::arm) {
diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
index cb8ba05d461d..69885aa53ca3 100644
--- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
+++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.cpp
@@ -12,6 +12,7 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Target.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/DenseSet.h"
@@ -233,6 +234,40 @@ void ObjectFileJSON::CreateSections(SectionList &unified_section_list) {
}
}
+bool ObjectFileJSON::SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) {
+ Log *log(GetLog(LLDBLog::DynamicLoader));
+ if (!m_sections_up)
+ return true;
+
+ addr_t slide = value;
+ if (!value_is_offset) {
+ addr_t lowest_addr = LLDB_INVALID_ADDRESS;
+ for (const SectionSP &section_sp : *m_sections_up) {
+ addr_t section_load_addr = section_sp->GetFileAddress();
+ lowest_addr = std::min(lowest_addr, section_load_addr);
+ }
+ if (lowest_addr == LLDB_INVALID_ADDRESS)
+ return false;
+ slide = value - lowest_addr;
+ }
+
+ // Apply slide to each section's file address.
+ for (const SectionSP &section_sp : *m_sections_up) {
+ addr_t section_load_addr = section_sp->GetFileAddress();
+ if (section_load_addr != LLDB_INVALID_ADDRESS) {
+ LLDB_LOGF(
+ log,
+ "ObjectFileJSON::SetLoadAddress section %s to load addr 0x%" PRIx64,
+ section_sp->GetName().AsCString(), section_load_addr + slide);
+ target.SetSectionLoadAddress(section_sp, section_load_addr + slide,
+ /*warn_multiple=*/true);
+ }
+ }
+
+ return true;
+}
+
bool ObjectFileJSON::MagicBytesMatch(DataBufferSP data_sp,
lldb::addr_t data_offset,
lldb::addr_t data_length) {
diff --git a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
index b72565f46886..029c8ff18893 100644
--- a/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
+++ b/lldb/source/Plugins/ObjectFile/JSON/ObjectFileJSON.h
@@ -86,6 +86,9 @@ public:
Strata CalculateStrata() override { return eStrataUser; }
+ bool SetLoadAddress(Target &target, lldb::addr_t value,
+ bool value_is_offset) override;
+
static bool MagicBytesMatch(lldb::DataBufferSP data_sp, lldb::addr_t offset,
lldb::addr_t length);
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index d7cb60e3f0c3..924e34053d41 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -2785,7 +2785,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
const char *symbol_name_non_abi_mangled = NULL;
SectionSP symbol_section;
- uint32_t symbol_byte_size = 0;
bool add_nlist = true;
bool is_debug = ((nlist.n_type & N_STAB) != 0);
bool demangled_is_synthesized = false;
@@ -3437,61 +3436,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
if (symbol_section) {
const addr_t section_file_addr =
symbol_section->GetFileAddress();
- if (symbol_byte_size == 0 &&
- function_starts_count > 0) {
- addr_t symbol_lookup_file_addr = nlist.n_value;
- // Do an exact address match for non-ARM addresses,
- // else get the closest since the symbol might be a
- // thumb symbol which has an address with bit zero
- // set
- FunctionStarts::Entry *func_start_entry =
- function_starts.FindEntry(symbol_lookup_file_addr,
- !is_arm);
- if (is_arm && func_start_entry) {
- // Verify that the function start address is the
- // symbol address (ARM) or the symbol address + 1
- // (thumb)
- if (func_start_entry->addr !=
- symbol_lookup_file_addr &&
- func_start_entry->addr !=
- (symbol_lookup_file_addr + 1)) {
- // Not the right entry, NULL it out...
- func_start_entry = NULL;
- }
- }
- if (func_start_entry) {
- func_start_entry->data = true;
-
- addr_t symbol_file_addr = func_start_entry->addr;
- uint32_t symbol_flags = 0;
- if (is_arm) {
- if (symbol_file_addr & 1)
- symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB;
- symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
- }
-
- const FunctionStarts::Entry *next_func_start_entry =
- function_starts.FindNextEntry(func_start_entry);
- const addr_t section_end_file_addr =
- section_file_addr +
- symbol_section->GetByteSize();
- if (next_func_start_entry) {
- addr_t next_symbol_file_addr =
- next_func_start_entry->addr;
- // Be sure the clear the Thumb address bit when
- // we calculate the size from the current and
- // next address
- if (is_arm)
- next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
- symbol_byte_size = std::min<lldb::addr_t>(
- next_symbol_file_addr - symbol_file_addr,
- section_end_file_addr - symbol_file_addr);
- } else {
- symbol_byte_size =
- section_end_file_addr - symbol_file_addr;
- }
- }
- }
symbol_value -= section_file_addr;
}
@@ -3620,9 +3564,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
}
sym[sym_idx].SetFlags(nlist.n_type << 16 | nlist.n_desc);
- if (symbol_byte_size > 0)
- sym[sym_idx].SetByteSize(symbol_byte_size);
-
if (demangled_is_synthesized)
sym[sym_idx].SetDemangledNameIsSynthesized(true);
++sym_idx;
@@ -3711,7 +3652,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
SymbolType type = eSymbolTypeInvalid;
SectionSP symbol_section;
- lldb::addr_t symbol_byte_size = 0;
bool add_nlist = true;
bool is_gsym = false;
bool demangled_is_synthesized = false;
@@ -4297,47 +4237,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
if (symbol_section) {
const addr_t section_file_addr = symbol_section->GetFileAddress();
- if (symbol_byte_size == 0 && function_starts_count > 0) {
- addr_t symbol_lookup_file_addr = nlist.n_value;
- // Do an exact address match for non-ARM addresses, else get the
- // closest since the symbol might be a thumb symbol which has an
- // address with bit zero set.
- FunctionStarts::Entry *func_start_entry =
- function_starts.FindEntry(symbol_lookup_file_addr, !is_arm);
- if (is_arm && func_start_entry) {
- // Verify that the function start address is the symbol address
- // (ARM) or the symbol address + 1 (thumb).
- if (func_start_entry->addr != symbol_lookup_file_addr &&
- func_start_entry->addr != (symbol_lookup_file_addr + 1)) {
- // Not the right entry, NULL it out...
- func_start_entry = nullptr;
- }
- }
- if (func_start_entry) {
- func_start_entry->data = true;
-
- addr_t symbol_file_addr = func_start_entry->addr;
- if (is_arm)
- symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
-
- const FunctionStarts::Entry *next_func_start_entry =
- function_starts.FindNextEntry(func_start_entry);
- const addr_t section_end_file_addr =
- section_file_addr + symbol_section->GetByteSize();
- if (next_func_start_entry) {
- addr_t next_symbol_file_addr = next_func_start_entry->addr;
- // Be sure the clear the Thumb address bit when we calculate the
- // size from the current and next address
- if (is_arm)
- next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
- symbol_byte_size = std::min<lldb::addr_t>(
- next_symbol_file_addr - symbol_file_addr,
- section_end_file_addr - symbol_file_addr);
- } else {
- symbol_byte_size = section_end_file_addr - symbol_file_addr;
- }
- }
- }
symbol_value -= section_file_addr;
}
@@ -4444,9 +4343,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
if (nlist.n_desc & N_WEAK_REF)
sym[sym_idx].SetIsWeak(true);
- if (symbol_byte_size > 0)
- sym[sym_idx].SetByteSize(symbol_byte_size);
-
if (demangled_is_synthesized)
sym[sym_idx].SetDemangledNameIsSynthesized(true);
@@ -4565,23 +4461,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
Address symbol_addr;
if (module_sp->ResolveFileAddress(symbol_file_addr, symbol_addr)) {
SectionSP symbol_section(symbol_addr.GetSection());
- uint32_t symbol_byte_size = 0;
if (symbol_section) {
- const addr_t section_file_addr = symbol_section->GetFileAddress();
- const FunctionStarts::Entry *next_func_start_entry =
- function_starts.FindNextEntry(func_start_entry);
- const addr_t section_end_file_addr =
- section_file_addr + symbol_section->GetByteSize();
- if (next_func_start_entry) {
- addr_t next_symbol_file_addr = next_func_start_entry->addr;
- if (is_arm)
- next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
- symbol_byte_size = std::min<lldb::addr_t>(
- next_symbol_file_addr - symbol_file_addr,
- section_end_file_addr - symbol_file_addr);
- } else {
- symbol_byte_size = section_end_file_addr - symbol_file_addr;
- }
sym[sym_idx].SetID(synthetic_sym_id++);
// Don't set the name for any synthetic symbols, the Symbol
// object will generate one if needed when the name is accessed
@@ -4593,8 +4473,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
add_symbol_addr(symbol_addr.GetFileAddress());
if (symbol_flags)
sym[sym_idx].SetFlags(symbol_flags);
- if (symbol_byte_size)
- sym[sym_idx].SetByteSize(symbol_byte_size);
++sym_idx;
}
}
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
index 777b20e9bb0f..492b44186720 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
@@ -71,6 +71,47 @@ GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
return std::string(toStringRef(llvm::ArrayRef(str_storage)));
}
+/// An "init expr" refers to a constant expression used to determine the initial
+/// value of certain elements within a module during instantiation. These
+/// expressions are restricted to operations that can be evaluated at module
+/// instantiation time. Currently we only support simple constant opcodes.
+static lldb::offset_t GetWasmOffsetFromInitExpr(DataExtractor &data,
+ lldb::offset_t &offset) {
+ lldb::offset_t init_expr_offset = LLDB_INVALID_OFFSET;
+
+ uint8_t opcode = data.GetU8(&offset);
+ switch (opcode) {
+ case llvm::wasm::WASM_OPCODE_I32_CONST:
+ case llvm::wasm::WASM_OPCODE_I64_CONST:
+ init_expr_offset = data.GetSLEB128(&offset);
+ break;
+ case llvm::wasm::WASM_OPCODE_GLOBAL_GET:
+ init_expr_offset = data.GetULEB128(&offset);
+ break;
+ case llvm::wasm::WASM_OPCODE_F32_CONST:
+ case llvm::wasm::WASM_OPCODE_F64_CONST:
+ // Not a meaningful offset.
+ data.GetFloat(&offset);
+ break;
+ case llvm::wasm::WASM_OPCODE_REF_NULL:
+ // Not a meaningful offset.
+ data.GetULEB128(&offset);
+ break;
+ }
+
+ // Make sure the opcodes we read aren't part of an extended init expr.
+ opcode = data.GetU8(&offset);
+ if (opcode == llvm::wasm::WASM_OPCODE_END)
+ return init_expr_offset;
+
+ // Extended init expressions are not supported, but we still have to parse
+ // them to skip over them and read the next segment.
+ do {
+ opcode = data.GetU8(&offset);
+ } while (opcode != llvm::wasm::WASM_OPCODE_END);
+ return LLDB_INVALID_OFFSET;
+}
+
/// Checks whether the data buffer starts with a valid Wasm module header.
static bool ValidateModuleHeader(const DataBufferSP &data_sp) {
if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize)
@@ -261,17 +302,20 @@ bool ObjectFileWasm::ParseHeader() {
return true;
}
-static llvm::Expected<std::vector<AddressRange>>
-ParseFunctions(SectionSP code_section_sp) {
- DataExtractor data;
- code_section_sp->GetSectionData(data);
+struct WasmFunction {
+ lldb::offset_t section_offset = LLDB_INVALID_OFFSET;
+ uint32_t size = 0;
+};
+
+static llvm::Expected<std::vector<WasmFunction>>
+ParseFunctions(DataExtractor &data) {
lldb::offset_t offset = 0;
llvm::Expected<uint32_t> function_count = GetULEB32(data, offset);
if (!function_count)
return function_count.takeError();
- std::vector<AddressRange> functions;
+ std::vector<WasmFunction> functions;
functions.reserve(*function_count);
for (uint32_t i = 0; i < *function_count; ++i) {
@@ -281,7 +325,7 @@ ParseFunctions(SectionSP code_section_sp) {
// llvm-objdump considers the ULEB with the function size to be part of the
// function. We can't do that here because that would break symbolic
// breakpoints, as that address is never executed.
- functions.emplace_back(code_section_sp, offset, *function_size);
+ functions.push_back({offset, *function_size});
std::optional<lldb::offset_t> next_offset =
llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
@@ -294,17 +338,22 @@ ParseFunctions(SectionSP code_section_sp) {
}
struct WasmSegment {
- WasmSegment(SectionSP section_sp, lldb::offset_t offset, uint32_t size)
- : address_range(section_sp, offset, size) {};
+ enum SegmentType {
+ Active,
+ Passive,
+ };
+
std::string name;
- AddressRange address_range;
-};
+ SegmentType type = Passive;
+ lldb::offset_t section_offset = LLDB_INVALID_OFFSET;
+ uint32_t size = 0;
+ uint32_t memory_index = 0;
+ lldb::offset_t init_expr_offset = 0;
-static llvm::Expected<std::vector<WasmSegment>>
-ParseData(SectionSP data_section_sp) {
- DataExtractor data;
- data_section_sp->GetSectionData(data);
+ lldb::offset_t GetFileOffset() const { return section_offset & 0xffffffff; }
+};
+static llvm::Expected<std::vector<WasmSegment>> ParseData(DataExtractor &data) {
lldb::offset_t offset = 0;
llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset);
@@ -319,27 +368,34 @@ ParseData(SectionSP data_section_sp) {
if (!flags)
return flags.takeError();
+ WasmSegment segment;
+
// Data segments have a mode that identifies them as either passive or
// active. An active data segment copies its contents into a memory during
// instantiation, as specified by a memory index and a constant expression
// defining an offset into that memory.
+ segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE)
+ ? WasmSegment::Passive
+ : WasmSegment::Active;
+
if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) {
+ assert(segment.type == WasmSegment::Active);
llvm::Expected<uint32_t> memidx = GetULEB32(data, offset);
if (!memidx)
return memidx.takeError();
+ segment.memory_index = *memidx;
}
- if ((*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) == 0) {
- // Skip over the constant expression.
- for (uint8_t b = 0; b != llvm::wasm::WASM_OPCODE_END;)
- b = data.GetU8(&offset);
- }
+ if (segment.type == WasmSegment::Active)
+ segment.init_expr_offset = GetWasmOffsetFromInitExpr(data, offset);
llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset);
if (!segment_size)
return segment_size.takeError();
- segments.emplace_back(data_section_sp, offset, *segment_size);
+ segment.section_offset = offset;
+ segment.size = *segment_size;
+ segments.push_back(segment);
std::optional<lldb::offset_t> next_offset =
llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size);
@@ -352,13 +408,11 @@ ParseData(SectionSP data_section_sp) {
}
static llvm::Expected<std::vector<Symbol>>
-ParseNames(SectionSP name_section_sp,
- const std::vector<AddressRange> &function_ranges,
+ParseNames(SectionSP code_section_sp, DataExtractor &name_data,
+ const std::vector<WasmFunction> &functions,
std::vector<WasmSegment> &segments) {
- DataExtractor name_section_data;
- name_section_sp->GetSectionData(name_section_data);
- llvm::DataExtractor data = name_section_data.GetAsLLVM();
+ llvm::DataExtractor data = name_data.GetAsLLVM();
llvm::DataExtractor::Cursor c(0);
std::vector<Symbol> symbols;
while (c && c.tell() < data.size()) {
@@ -380,12 +434,13 @@ ParseNames(SectionSP name_section_sp,
llvm::Expected<std::string> name = GetWasmString(data, c);
if (!name)
return name.takeError();
- if (*idx >= function_ranges.size())
+ if (*idx >= functions.size())
continue;
symbols.emplace_back(
- symbols.size(), Mangled(*name), lldb::eSymbolTypeCode,
+ symbols.size(), *name, lldb::eSymbolTypeCode,
/*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false,
- /*is_artificial=*/false, function_ranges[*idx],
+ /*is_artificial=*/false, code_section_sp,
+ functions[i].section_offset, functions[i].size,
/*size_is_valid=*/true, /*contains_linker_annotations=*/false,
/*flags=*/0);
}
@@ -405,12 +460,6 @@ ParseNames(SectionSP name_section_sp,
continue;
// Update the segment name.
segments[i].name = *name;
- symbols.emplace_back(
- symbols.size(), Mangled(*name), lldb::eSymbolTypeData,
- /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false,
- /*is_artificial=*/false, segments[i].address_range,
- /*size_is_valid=*/true, /*contains_linker_annotations=*/false,
- /*flags=*/0);
}
} break;
@@ -432,80 +481,11 @@ ParseNames(SectionSP name_section_sp,
}
void ObjectFileWasm::ParseSymtab(Symtab &symtab) {
- assert(m_sections_up && "sections must be parsed");
- Log *log = GetLog(LLDBLog::Object);
-
- // The name section contains names and indexes. First parse the data from the
- // relevant sections so we can access it by its index.
- std::vector<AddressRange> functions;
- std::vector<WasmSegment> segments;
-
- // Parse the code section.
- if (SectionSP code_section_sp =
- m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false)) {
- llvm::Expected<std::vector<AddressRange>> maybe_functions =
- ParseFunctions(code_section_sp);
- if (!maybe_functions) {
- LLDB_LOG_ERROR(log, maybe_functions.takeError(),
- "Failed to parse Wasm code section: {0}");
- return;
- }
- functions = *maybe_functions;
- }
-
- // Parse the data section.
- SectionSP data_section_sp =
- m_sections_up->FindSectionByType(lldb::eSectionTypeData, false);
- if (data_section_sp) {
- llvm::Expected<std::vector<WasmSegment>> maybe_segments =
- ParseData(data_section_sp);
- if (!maybe_segments) {
- LLDB_LOG_ERROR(log, maybe_segments.takeError(),
- "Failed to parse Wasm data section: {0}");
- return;
- }
- segments = *maybe_segments;
- }
-
- // Parse the name section.
- SectionSP name_section_sp =
- m_sections_up->FindSectionByType(lldb::eSectionTypeWasmName, false);
- if (!name_section_sp) {
- LLDB_LOG(log, "Failed to parse Wasm symbol table: no names section");
- return;
- }
-
- llvm::Expected<std::vector<Symbol>> symbols =
- ParseNames(name_section_sp, functions, segments);
- if (!symbols) {
- LLDB_LOG_ERROR(log, symbols.takeError(), "Failed to parse Wasm names: {0}");
- return;
- }
-
- for (const Symbol &symbol : *symbols)
+ for (const Symbol &symbol : m_symbols)
symtab.AddSymbol(symbol);
- lldb::user_id_t segment_id = 0;
- for (const WasmSegment &segment : segments) {
- const lldb::addr_t segment_addr =
- segment.address_range.GetBaseAddress().GetFileAddress();
- const size_t segment_size = segment.address_range.GetByteSize();
- SectionSP segment_sp = std::make_shared<Section>(
- /*parent_section_sp=*/data_section_sp, GetModule(),
- /*obj_file=*/this,
- ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid
- // collision with section IDs.
- ConstString(segment.name), eSectionTypeData,
- /*file_vm_addr=*/segment_addr,
- /*vm_size=*/segment_size,
- /*file_offset=*/segment_addr,
- /*file_size=*/segment_size,
- /*log2align=*/0, /*flags=*/0);
- m_sections_up->AddSection(segment_sp);
- GetModule()->GetSectionList()->AddSection(segment_sp);
- }
-
symtab.Finalize();
+ m_symbols.clear();
}
static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
@@ -516,7 +496,27 @@ static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
return eSectionTypeOther;
}
+std::optional<ObjectFileWasm::section_info>
+ObjectFileWasm::GetSectionInfo(uint32_t section_id) {
+ for (const section_info &sect_info : m_sect_infos) {
+ if (sect_info.id == section_id)
+ return sect_info;
+ }
+ return std::nullopt;
+}
+
+std::optional<ObjectFileWasm::section_info>
+ObjectFileWasm::GetSectionInfo(llvm::StringRef section_name) {
+ for (const section_info &sect_info : m_sect_infos) {
+ if (sect_info.name == section_name)
+ return sect_info;
+ }
+ return std::nullopt;
+}
+
void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
+ Log *log = GetLog(LLDBLog::Object);
+
if (m_sections_up)
return;
@@ -530,7 +530,7 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
SectionType section_type = eSectionTypeOther;
ConstString section_name;
offset_t file_offset = sect_info.offset & 0xffffffff;
- addr_t vm_addr = file_offset;
+ addr_t vm_addr = sect_info.offset;
size_t vm_size = sect_info.size;
if (llvm::wasm::WASM_SEC_CODE == sect_info.id) {
@@ -542,9 +542,6 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
// For this reason Section::GetFileAddress() must return zero for the
// Code section.
vm_addr = 0;
- } else if (llvm::wasm::WASM_SEC_DATA == sect_info.id) {
- section_type = eSectionTypeData;
- section_name = ConstString("data");
} else {
section_type = GetSectionTypeFromName(sect_info.name.GetStringRef());
if (section_type == eSectionTypeOther)
@@ -556,23 +553,107 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
}
}
- SectionSP section_sp(
- new Section(GetModule(), // Module to which this section belongs.
- this, // ObjectFile to which this section belongs and
- // should read section data from.
- section_type, // Section ID.
- section_name, // Section name.
- section_type, // Section type.
- vm_addr, // VM address.
- vm_size, // VM size in bytes of this section.
- file_offset, // Offset of this section in the file.
- sect_info.size, // Size of the section as found in the file.
- 0, // Alignment of the section
- 0, // Flags for this section.
- 1)); // Number of host bytes per target byte
+ SectionSP section_sp = std::make_shared<Section>(
+ GetModule(), // Module to which this section belongs.
+ this, // ObjectFile to which this section belongs and
+ // should read section data from.
+ section_type, // Section ID.
+ section_name, // Section name.
+ section_type, // Section type.
+ vm_addr, // VM address.
+ vm_size, // VM size in bytes of this section.
+ file_offset, // Offset of this section in the file.
+ sect_info.size, // Size of the section as found in the file.
+ 0, // Alignment of the section
+ 0, // Flags for this section.
+ 1); // Number of host bytes per target byte
m_sections_up->AddSection(section_sp);
unified_section_list.AddSection(section_sp);
}
+
+ // The name section contains names and indexes. First parse the data from the
+ // relevant sections so we can access it by its index.
+ std::vector<WasmFunction> functions;
+ std::vector<WasmSegment> segments;
+
+ // Parse the code section.
+ if (std::optional<section_info> info =
+ GetSectionInfo(llvm::wasm::WASM_SEC_CODE)) {
+ DataExtractor code_data = ReadImageData(info->offset, info->size);
+ llvm::Expected<std::vector<WasmFunction>> maybe_functions =
+ ParseFunctions(code_data);
+ if (!maybe_functions) {
+ LLDB_LOG_ERROR(log, maybe_functions.takeError(),
+ "Failed to parse Wasm code section: {0}");
+ } else {
+ functions = *maybe_functions;
+ }
+ }
+
+ // Parse the data section.
+ std::optional<section_info> data_info =
+ GetSectionInfo(llvm::wasm::WASM_SEC_DATA);
+ if (data_info) {
+ DataExtractor data_data = ReadImageData(data_info->offset, data_info->size);
+ llvm::Expected<std::vector<WasmSegment>> maybe_segments =
+ ParseData(data_data);
+ if (!maybe_segments) {
+ LLDB_LOG_ERROR(log, maybe_segments.takeError(),
+ "Failed to parse Wasm data section: {0}");
+ } else {
+ segments = *maybe_segments;
+ }
+ }
+
+ if (std::optional<section_info> info = GetSectionInfo("name")) {
+ DataExtractor names_data = ReadImageData(info->offset, info->size);
+ llvm::Expected<std::vector<Symbol>> symbols = ParseNames(
+ m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false),
+ names_data, functions, segments);
+ if (!symbols) {
+ LLDB_LOG_ERROR(log, symbols.takeError(),
+ "Failed to parse Wasm names: {0}");
+ } else {
+ m_symbols = *symbols;
+ }
+ }
+
+ lldb::user_id_t segment_id = 0;
+ for (const WasmSegment &segment : segments) {
+ if (segment.type == WasmSegment::Active) {
+ // FIXME: Support segments with a memory index.
+ if (segment.memory_index != 0) {
+ LLDB_LOG(log, "Skipping segment {0}: non-zero memory index is "
+ "currently unsupported");
+ continue;
+ }
+
+ if (segment.init_expr_offset == LLDB_INVALID_OFFSET) {
+ LLDB_LOG(log, "Skipping segment {0}: unsupported init expression");
+ continue;
+ }
+ }
+
+ const lldb::addr_t file_vm_addr =
+ segment.type == WasmSegment::Active
+ ? segment.init_expr_offset
+ : data_info->offset + segment.section_offset;
+ const lldb::offset_t file_offset =
+ data_info->GetFileOffset() + segment.GetFileOffset();
+ SectionSP segment_sp = std::make_shared<Section>(
+ GetModule(),
+ /*obj_file=*/this,
+ ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid
+ // collision with section IDs.
+ ConstString(segment.name), eSectionTypeData,
+ /*file_vm_addr=*/file_vm_addr,
+ /*vm_size=*/segment.size,
+ /*file_offset=*/file_offset,
+ /*file_size=*/segment.size,
+ /*log2align=*/0, /*flags=*/0);
+ m_sections_up->AddSection(segment_sp);
+ GetModule()->GetSectionList()->AddSection(segment_sp);
+ }
}
bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address,
@@ -697,7 +778,7 @@ void ObjectFileWasm::Dump(Stream *s) {
}
void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream,
- const section_info_t &sh) {
+ const section_info &sh) {
ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " "
<< llvm::format_hex(sh.offset, 10) << " "
<< llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6)
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h
index 531b5f0437a4..86ecbf26803c 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h
@@ -128,20 +128,25 @@ private:
/// Read a range of bytes from the Wasm module.
DataExtractor ReadImageData(lldb::offset_t offset, uint32_t size);
- typedef struct section_info {
+ struct section_info {
lldb::offset_t offset;
uint32_t size;
uint32_t id;
ConstString name;
- } section_info_t;
+ lldb::offset_t GetFileOffset() const { return offset & 0xffffffff; }
+ };
+
+ std::optional<section_info> GetSectionInfo(uint32_t section_id);
+ std::optional<section_info> GetSectionInfo(llvm::StringRef section_name);
/// Wasm section header dump routines.
/// \{
- void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info_t &sh);
+ void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info &sh);
void DumpSectionHeaders(llvm::raw_ostream &ostream);
/// \}
- std::vector<section_info_t> m_sect_infos;
+ std::vector<section_info> m_sect_infos;
+ std::vector<Symbol> m_symbols;
ArchSpec m_arch;
UUID m_uuid;
};
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index fdafacf410d6..c1bc6a3f036b 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
@@ -76,7 +76,7 @@ NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm(
::memset(&m_fpr, 0, sizeof(m_fpr));
::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm));
::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs));
- ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs));
+ ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs));
// 16 is just a maximum value, query hardware for actual watchpoint count
m_max_hwp_supported = 16;
@@ -283,522 +283,43 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const {
return false;
}
-uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareBreakpoints() {
- Log *log = GetLog(POSIXLog::Breakpoints);
-
- LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
-
- Status error;
-
- // Read hardware breakpoint and watchpoint information.
- error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return 0;
-
- LLDB_LOG(log, "{0}", m_max_hbp_supported);
- return m_max_hbp_supported;
-}
-
-uint32_t
-NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr,
- size_t size) {
- Log *log = GetLog(POSIXLog::Breakpoints);
- LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size);
-
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return LLDB_INVALID_INDEX32;
-
- uint32_t control_value = 0, bp_index = 0;
-
- // Setup address and control values.
- // Use size to get a hint of arm vs thumb modes.
- switch (size) {
- case 2:
- control_value = (0x3 << 5) | 7;
- addr &= ~1;
- break;
- case 4:
- control_value = (0xfu << 5) | 7;
- addr &= ~3;
- break;
- default:
- return LLDB_INVALID_INDEX32;
- }
-
- // Iterate over stored breakpoints and find a free bp_index
- bp_index = LLDB_INVALID_INDEX32;
- for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
- if ((m_hbr_regs[i].control & 1) == 0) {
- bp_index = i; // Mark last free slot
- } else if (m_hbr_regs[i].address == addr) {
- return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints.
- }
- }
-
- if (bp_index == LLDB_INVALID_INDEX32)
- return LLDB_INVALID_INDEX32;
-
- // Update breakpoint in local cache
- m_hbr_regs[bp_index].real_addr = addr;
- m_hbr_regs[bp_index].address = addr;
- m_hbr_regs[bp_index].control = control_value;
-
- // PTRACE call to set corresponding hardware breakpoint register.
- error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
- bp_index);
-
- if (error.Fail()) {
- m_hbr_regs[bp_index].address = 0;
- m_hbr_regs[bp_index].control &= ~1;
-
- return LLDB_INVALID_INDEX32;
- }
-
- return bp_index;
-}
-
-bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) {
- Log *log = GetLog(POSIXLog::Breakpoints);
- LLDB_LOG(log, "hw_idx: {0}", hw_idx);
-
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return false;
-
- if (hw_idx >= m_max_hbp_supported)
- return false;
-
- // Create a backup we can revert to in case of failure.
- lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address;
- uint32_t tempControl = m_hbr_regs[hw_idx].control;
-
- m_hbr_regs[hw_idx].control &= ~1;
- m_hbr_regs[hw_idx].address = 0;
-
- // PTRACE call to clear corresponding hardware breakpoint register.
- error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK,
- hw_idx);
-
- if (error.Fail()) {
- m_hbr_regs[hw_idx].control = tempControl;
- m_hbr_regs[hw_idx].address = tempAddr;
-
- return false;
- }
-
- return true;
-}
-
-Status NativeRegisterContextLinux_arm::GetHardwareBreakHitIndex(
- uint32_t &bp_index, lldb::addr_t trap_addr) {
- Log *log = GetLog(POSIXLog::Breakpoints);
-
- LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
-
- lldb::addr_t break_addr;
-
- for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) {
- break_addr = m_hbr_regs[bp_index].address;
-
- if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) {
- m_hbr_regs[bp_index].hit_addr = trap_addr;
- return Status();
- }
- }
-
- bp_index = LLDB_INVALID_INDEX32;
- return Status();
-}
-
-Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() {
- Log *log = GetLog(POSIXLog::Breakpoints);
-
- LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__);
-
- Status error;
-
- // Read hardware breakpoint and watchpoint information.
- error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return error;
-
- lldb::addr_t tempAddr = 0;
- uint32_t tempControl = 0;
-
- for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
- if (m_hbr_regs[i].control & 0x01) {
- // Create a backup we can revert to in case of failure.
- tempAddr = m_hbr_regs[i].address;
- tempControl = m_hbr_regs[i].control;
-
- // Clear breakpoints in local cache
- m_hbr_regs[i].control &= ~1;
- m_hbr_regs[i].address = 0;
-
- // Ptrace call to update hardware debug registers
- error =
- WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i);
-
- if (error.Fail()) {
- m_hbr_regs[i].control = tempControl;
- m_hbr_regs[i].address = tempAddr;
-
- return error;
- }
- }
- }
-
- return Status();
-}
-
-uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints() {
- Log *log = GetLog(POSIXLog::Watchpoints);
-
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return 0;
-
- LLDB_LOG(log, "{0}", m_max_hwp_supported);
- return m_max_hwp_supported;
-}
-
-uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint(
- lldb::addr_t addr, size_t size, uint32_t watch_flags) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size,
- watch_flags);
-
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return LLDB_INVALID_INDEX32;
-
- uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0;
- lldb::addr_t real_addr = addr;
-
- // Check if we are setting watchpoint other than read/write/access Also
- // update watchpoint flag to match Arm write-read bit configuration.
- switch (watch_flags) {
- case 1:
- watch_flags = 2;
- break;
- case 2:
- watch_flags = 1;
- break;
- case 3:
- break;
- default:
- return LLDB_INVALID_INDEX32;
- }
-
- // Can't watch zero bytes
- // Can't watch more than 4 bytes per WVR/WCR pair
-
- if (size == 0 || size > 4)
- return LLDB_INVALID_INDEX32;
-
- // Check 4-byte alignment for hardware watchpoint target address. Below is a
- // hack to recalculate address and size in order to make sure we can watch
- // non 4-byte aligned addresses as well.
- if (addr & 0x03) {
- uint8_t watch_mask = (addr & 0x03) + size;
-
- if (watch_mask > 0x04)
- return LLDB_INVALID_INDEX32;
- else if (watch_mask <= 0x02)
- size = 2;
- else
- size = 4;
-
- addr = addr & (~0x03);
- }
-
- // We can only watch up to four bytes that follow a 4 byte aligned address
- // per watchpoint register pair, so make sure we can properly encode this.
- addr_word_offset = addr % 4;
- byte_mask = ((1u << size) - 1u) << addr_word_offset;
-
- // Check if we need multiple watchpoint register
- if (byte_mask > 0xfu)
- return LLDB_INVALID_INDEX32;
-
- // Setup control value
- // Make the byte_mask into a valid Byte Address Select mask
- control_value = byte_mask << 5;
-
- // Turn on appropriate watchpoint flags read or write
- control_value |= (watch_flags << 3);
-
- // Enable this watchpoint and make it stop in privileged or user mode;
- control_value |= 7;
-
- // Make sure bits 1:0 are clear in our address
- addr &= ~((lldb::addr_t)3);
-
- // Iterate over stored watchpoints and find a free wp_index
- wp_index = LLDB_INVALID_INDEX32;
- for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
- if ((m_hwp_regs[i].control & 1) == 0) {
- wp_index = i; // Mark last free slot
- } else if (m_hwp_regs[i].address == addr) {
- return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints.
- }
- }
-
- if (wp_index == LLDB_INVALID_INDEX32)
- return LLDB_INVALID_INDEX32;
-
- // Update watchpoint in local cache
- m_hwp_regs[wp_index].real_addr = real_addr;
- m_hwp_regs[wp_index].address = addr;
- m_hwp_regs[wp_index].control = control_value;
-
- // PTRACE call to set corresponding watchpoint register.
- error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
- wp_index);
-
- if (error.Fail()) {
- m_hwp_regs[wp_index].address = 0;
- m_hwp_regs[wp_index].control &= ~1;
-
- return LLDB_INVALID_INDEX32;
- }
-
- return wp_index;
-}
-
-bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint(
- uint32_t wp_index) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
-
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return false;
-
- if (wp_index >= m_max_hwp_supported)
- return false;
-
- // Create a backup we can revert to in case of failure.
- lldb::addr_t tempAddr = m_hwp_regs[wp_index].address;
- uint32_t tempControl = m_hwp_regs[wp_index].control;
-
- // Update watchpoint in local cache
- m_hwp_regs[wp_index].control &= ~1;
- m_hwp_regs[wp_index].address = 0;
-
- // Ptrace call to update hardware debug registers
- error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH,
- wp_index);
-
- if (error.Fail()) {
- m_hwp_regs[wp_index].control = tempControl;
- m_hwp_regs[wp_index].address = tempAddr;
-
- return false;
- }
-
- return true;
-}
-
-Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() {
- // Read hardware breakpoint and watchpoint information.
- Status error = ReadHardwareDebugInfo();
-
- if (error.Fail())
- return error;
-
- lldb::addr_t tempAddr = 0;
- uint32_t tempControl = 0;
-
- for (uint32_t i = 0; i < m_max_hwp_supported; i++) {
- if (m_hwp_regs[i].control & 0x01) {
- // Create a backup we can revert to in case of failure.
- tempAddr = m_hwp_regs[i].address;
- tempControl = m_hwp_regs[i].control;
-
- // Clear watchpoints in local cache
- m_hwp_regs[i].control &= ~1;
- m_hwp_regs[i].address = 0;
-
- // Ptrace call to update hardware debug registers
- error =
- WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i);
-
- if (error.Fail()) {
- m_hwp_regs[i].control = tempControl;
- m_hwp_regs[i].address = tempAddr;
-
- return error;
- }
- }
- }
-
- return Status();
-}
-
-uint32_t NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
-
- switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) {
- case 0x01:
- return 1;
- case 0x03:
- return 2;
- case 0x07:
- return 3;
- case 0x0f:
- return 4;
- default:
- return 0;
- }
-}
-bool NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
-
- if ((m_hwp_regs[wp_index].control & 0x1) == 0x1)
- return true;
- else
- return false;
-}
-
-Status
-NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index,
- lldb::addr_t trap_addr) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr);
-
- uint32_t watch_size;
- lldb::addr_t watch_addr;
-
- for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) {
- watch_size = GetWatchpointSize(wp_index);
- watch_addr = m_hwp_regs[wp_index].address;
-
- if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr &&
- trap_addr < watch_addr + watch_size) {
- m_hwp_regs[wp_index].hit_addr = trap_addr;
- return Status();
- }
- }
-
- wp_index = LLDB_INVALID_INDEX32;
- return Status();
-}
-
-lldb::addr_t
-NativeRegisterContextLinux_arm::GetWatchpointAddress(uint32_t wp_index) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
-
- if (wp_index >= m_max_hwp_supported)
- return LLDB_INVALID_ADDRESS;
-
- if (WatchpointIsEnabled(wp_index))
- return m_hwp_regs[wp_index].real_addr;
- else
- return LLDB_INVALID_ADDRESS;
-}
-
-lldb::addr_t
-NativeRegisterContextLinux_arm::GetWatchpointHitAddress(uint32_t wp_index) {
- Log *log = GetLog(POSIXLog::Watchpoints);
- LLDB_LOG(log, "wp_index: {0}", wp_index);
-
- if (wp_index >= m_max_hwp_supported)
- return LLDB_INVALID_ADDRESS;
-
- if (WatchpointIsEnabled(wp_index))
- return m_hwp_regs[wp_index].hit_addr;
- else
- return LLDB_INVALID_ADDRESS;
-}
-
-Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
- Status error;
-
- if (!m_refresh_hwdebug_info) {
- return Status();
- }
+llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() {
+ if (!m_refresh_hwdebug_info)
+ return llvm::Error::success();
#ifdef __arm__
unsigned int cap_val;
-
- error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(),
- nullptr, &cap_val,
- sizeof(unsigned int));
+ Status error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val,
+ sizeof(unsigned int));
if (error.Fail())
- return error;
+ return error.ToError();
m_max_hwp_supported = (cap_val >> 8) & 0xff;
m_max_hbp_supported = cap_val & 0xff;
m_refresh_hwdebug_info = false;
- return error;
+ return error.ToError();
#else // __aarch64__
return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported,
- m_max_hbp_supported);
+ m_max_hbp_supported)
+ .ToError();
#endif // ifdef __arm__
}
-Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(
- NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) {
- Status error;
-
+llvm::Error
+NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType) {
#ifdef __arm__
- lldb::addr_t *addr_buf;
- uint32_t *ctrl_buf;
-
- if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) {
- addr_buf = &m_hwp_regs[hwb_index].address;
- ctrl_buf = &m_hwp_regs[hwb_index].control;
-
- error = NativeProcessLinux::PtraceWrapper(
- PTRACE_SETHBPREGS, m_thread.GetID(),
- (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 1), addr_buf,
- sizeof(unsigned int));
-
- if (error.Fail())
- return error;
-
- error = NativeProcessLinux::PtraceWrapper(
- PTRACE_SETHBPREGS, m_thread.GetID(),
- (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf,
- sizeof(unsigned int));
- } else {
- addr_buf = &m_hbr_regs[hwb_index].address;
- ctrl_buf = &m_hbr_regs[hwb_index].control;
-
- error = NativeProcessLinux::PtraceWrapper(
- PTRACE_SETHBPREGS, m_thread.GetID(),
- (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 1), addr_buf,
- sizeof(unsigned int));
+ uint32_t max_index = m_max_hbp_supported;
+ if (hwbType == eDREGTypeWATCH)
+ max_index = m_max_hwp_supported;
- if (error.Fail())
+ for (uint32_t idx = 0; idx < max_index; ++idx)
+ if (auto error = WriteHardwareDebugReg(hwbType, idx))
return error;
- error = NativeProcessLinux::PtraceWrapper(
- PTRACE_SETHBPREGS, m_thread.GetID(),
- (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf,
- sizeof(unsigned int));
- }
-
- return error;
+ return llvm::Error::success();
#else // __aarch64__
uint32_t max_supported =
(hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
@@ -806,12 +327,48 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(
: m_max_hbp_supported;
auto &regs = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH)
? m_hwp_regs
- : m_hbr_regs;
+ : m_hbp_regs;
return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported,
- regs);
+ regs)
+ .ToError();
#endif // ifdef __arm__
}
+#ifdef __arm__
+llvm::Error
+NativeRegisterContextLinux_arm::WriteHardwareDebugReg(DREGType hwbType,
+ int hwb_index) {
+ Status error;
+ lldb::addr_t *addr_buf;
+ uint32_t *ctrl_buf;
+ int addr_idx = (hwb_index << 1) + 1;
+ int ctrl_idx = addr_idx + 1;
+
+ if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) {
+ addr_idx *= -1;
+ addr_buf = &m_hwp_regs[hwb_index].address;
+ ctrl_idx *= -1;
+ ctrl_buf = &m_hwp_regs[hwb_index].control;
+ } else {
+ addr_buf = &m_hbp_regs[hwb_index].address;
+ ctrl_buf = &m_hbp_regs[hwb_index].control;
+ }
+
+ error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)addr_idx,
+ addr_buf, sizeof(unsigned int));
+
+ if (error.Fail())
+ return error.ToError();
+
+ error = NativeProcessLinux::PtraceWrapper(
+ PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)ctrl_idx,
+ ctrl_buf, sizeof(unsigned int));
+
+ return error.ToError();
+}
+#endif // ifdef __arm__
+
uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset(
const RegisterInfo *reg_info) const {
return reg_info->byte_offset - GetGPRSize();
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
index 3a31d68d7a3c..cf36859b16ad 100644
--- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
+++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h
@@ -12,7 +12,7 @@
#define lldb_NativeRegisterContextLinux_arm_h
#include "Plugins/Process/Linux/NativeRegisterContextLinux.h"
-#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h"
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h"
#include "Plugins/Process/Utility/lldb-arm-register-enums.h"
@@ -21,7 +21,8 @@ namespace process_linux {
class NativeProcessLinux;
-class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux {
+class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux,
+ public NativeRegisterContextDBReg_arm {
public:
NativeRegisterContextLinux_arm(const ArchSpec &target_arch,
NativeThreadProtocol &native_thread);
@@ -42,39 +43,6 @@ public:
Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override;
- // Hardware breakpoints/watchpoint management functions
-
- uint32_t NumSupportedHardwareBreakpoints() override;
-
- uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override;
-
- bool ClearHardwareBreakpoint(uint32_t hw_idx) override;
-
- Status ClearAllHardwareBreakpoints() override;
-
- Status GetHardwareBreakHitIndex(uint32_t &bp_index,
- lldb::addr_t trap_addr) override;
-
- uint32_t NumSupportedHardwareWatchpoints() override;
-
- uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size,
- uint32_t watch_flags) override;
-
- bool ClearHardwareWatchpoint(uint32_t hw_index) override;
-
- Status ClearAllHardwareWatchpoints() override;
-
- Status GetWatchpointHitIndex(uint32_t &wp_index,
- lldb::addr_t trap_addr) override;
-
- lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override;
-
- lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override;
-
- uint32_t GetWatchpointSize(uint32_t wp_index);
-
- bool WatchpointIsEnabled(uint32_t wp_index);
-
protected:
Status DoReadRegisterValue(uint32_t offset, const char *reg_name,
uint32_t size, RegisterValue &value) override;
@@ -100,23 +68,18 @@ private:
uint32_t m_gpr_arm[k_num_gpr_registers_arm];
RegisterInfoPOSIX_arm::FPU m_fpr;
- std::array<NativeRegisterContextDBReg::DREG, 16>
- m_hbr_regs; // Arm native linux hardware breakpoints
- std::array<NativeRegisterContextDBReg::DREG, 16>
- m_hwp_regs; // Arm native linux hardware watchpoints
-
- uint32_t m_max_hwp_supported;
- uint32_t m_max_hbp_supported;
bool m_refresh_hwdebug_info;
bool IsGPR(unsigned reg) const;
bool IsFPR(unsigned reg) const;
- Status ReadHardwareDebugInfo();
+ llvm::Error ReadHardwareDebugInfo() override;
- Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType,
- int hwb_index);
+ llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;
+#ifdef __arm__
+ llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index);
+#endif
uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const;
diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
index 5d99c22dafe1..b1e326ec064e 100644
--- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt
@@ -13,6 +13,7 @@ add_lldb_library(lldbPluginProcessUtility
MemoryTagManagerAArch64MTE.cpp
NativeProcessSoftwareSingleStep.cpp
NativeRegisterContextDBReg.cpp
+ NativeRegisterContextDBReg_arm.cpp
NativeRegisterContextDBReg_arm64.cpp
NativeRegisterContextDBReg_loongarch.cpp
NativeRegisterContextDBReg_x86.cpp
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
index 19601b7f35d4..f35027e93514 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
@@ -44,15 +44,16 @@ uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr,
return LLDB_INVALID_INDEX32;
}
- uint32_t control_value = 0, bp_index = 0;
-
if (!ValidateBreakpoint(size, addr))
return LLDB_INVALID_INDEX32;
- control_value = MakeBreakControlValue(size);
+ uint32_t control_value = MakeBreakControlValue(size);
+ auto details = AdjustBreakpoint({size, addr});
+ size = details.size;
+ addr = details.addr;
// Iterate over stored breakpoints and find a free bp_index
- bp_index = LLDB_INVALID_INDEX32;
+ uint32_t bp_index = LLDB_INVALID_INDEX32;
for (uint32_t i = 0; i < m_max_hbp_supported; i++) {
if (!BreakpointIsEnabled(i))
bp_index = i; // Mark last free slot
@@ -222,7 +223,7 @@ uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint(
addr = adjusted->addr;
// Check if we are setting watchpoint other than read/write/access Also
- // update watchpoint flag to match AArch64/LoongArch write-read bit
+ // update watchpoint flag to match ARM/AArch64/LoongArch write-read bit
// configuration.
switch (watch_flags) {
case lldb::eWatchpointKindWrite:
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
index 9b6ecd382c3f..2dd11dcf74de 100644
--- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h
@@ -12,6 +12,7 @@
#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
#include <array>
+#include <optional>
// Common utilities for hardware breakpoints and hardware watchpoints on AArch64
// and LoongArch.
@@ -76,19 +77,25 @@ protected:
// On AArch64 and Loongarch the hardware breakpoint length size is 4, and the
// target address must 4-byte alignment.
- bool ValidateBreakpoint(size_t size, lldb::addr_t addr) {
+ virtual bool ValidateBreakpoint(size_t size, lldb::addr_t addr) {
return (size == 4) && !(addr & 0x3);
}
+
struct WatchpointDetails {
size_t size;
lldb::addr_t addr;
};
virtual std::optional<WatchpointDetails>
AdjustWatchpoint(const WatchpointDetails &details) = 0;
+
+ using BreakpointDetails = WatchpointDetails;
+ virtual BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) {
+ return details;
+ }
+
virtual uint32_t MakeBreakControlValue(size_t size) = 0;
virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0;
virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0;
-
virtual llvm::Error ReadHardwareDebugInfo() = 0;
virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0;
virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) {
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp
new file mode 100644
index 000000000000..803fed54d6b0
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp
@@ -0,0 +1,117 @@
+//===-- NativeRegisterContextDBReg_arm.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NativeRegisterContextDBReg_arm.h"
+
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RegisterValue.h"
+
+using namespace lldb_private;
+
+uint32_t NativeRegisterContextDBReg_arm::GetWatchpointSize(uint32_t wp_index) {
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOG(log, "wp_index: {0}", wp_index);
+
+ switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) {
+ case 0x01:
+ return 1;
+ case 0x03:
+ return 2;
+ case 0x07:
+ return 3;
+ case 0x0f:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+std::optional<NativeRegisterContextDBReg::WatchpointDetails>
+NativeRegisterContextDBReg_arm::AdjustWatchpoint(
+ const WatchpointDetails &details) {
+ auto [size, addr] = details;
+
+ if (size == 0 || size > 4)
+ return {};
+
+ // Check 4-byte alignment for hardware watchpoint target address. Below is a
+ // hack to recalculate address and size in order to make sure we can watch
+ // non 4-byte aligned addresses as well.
+ if (addr & 0x03) {
+ uint8_t watch_mask = (addr & 0x03) + size;
+ if (watch_mask > 0x04)
+ return {};
+ else if (watch_mask <= 0x02)
+ size = 2;
+ else
+ size = 4;
+
+ addr = addr & (~0x03);
+ }
+
+ return WatchpointDetails{size, addr};
+}
+
+NativeRegisterContextDBReg::BreakpointDetails
+NativeRegisterContextDBReg_arm::AdjustBreakpoint(
+ const BreakpointDetails &details) {
+ BreakpointDetails bd = details;
+ // Use size to get a hint of arm vs thumb modes.
+ // LLDB usually aligns this client side, but other clients may not.
+ switch (bd.size) {
+ case 2:
+ bd.addr &= ~1;
+ break;
+ case 4:
+ bd.addr &= ~3;
+ break;
+ default:
+ // We assume that ValidateBreakpoint would have caught this earlier.
+ llvm_unreachable("Invalid breakpoint size!");
+ }
+
+ return bd;
+}
+
+uint32_t NativeRegisterContextDBReg_arm::MakeBreakControlValue(size_t size) {
+ switch (size) {
+ case 2:
+ return (0x3 << 5) | 7;
+ case 4:
+ return (0xfu << 5) | 7;
+ default:
+ // ValidateBreakpoint would have rejected this earlier.
+ llvm_unreachable("Invalid breakpoint size.");
+ }
+}
+
+uint32_t
+NativeRegisterContextDBReg_arm::MakeWatchControlValue(size_t size,
+ uint32_t watch_flags) {
+ // We can only watch up to four bytes that follow a 4 byte aligned address
+ // per watchpoint register pair, so make sure we can properly encode this.
+ // We assume that the address was 4 byte aligned by AdjustWatchpoint.
+ uint32_t byte_mask = (1u << size) - 1u;
+
+ // Check if we need multiple watchpoint register
+ if (byte_mask > 0xfu)
+ return LLDB_INVALID_INDEX32;
+
+ // Setup control value
+ // Make the byte_mask into a valid Byte Address Select mask
+ uint32_t control_value = byte_mask << 5;
+
+ // Turn on appropriate watchpoint flags read or write
+ control_value |= (watch_flags << 3);
+
+ // Enable this watchpoint and make it stop in privileged or user mode;
+ control_value |= 7;
+
+ return control_value;
+}
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h
new file mode 100644
index 000000000000..253ae96c90c9
--- /dev/null
+++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h
@@ -0,0 +1,42 @@
+//===-- NativeRegisterContextDBReg_arm.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef lldb_NativeRegisterContextDBReg_arm_h
+#define lldb_NativeRegisterContextDBReg_arm_h
+
+#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h"
+
+namespace lldb_private {
+
+class NativeRegisterContextDBReg_arm : public NativeRegisterContextDBReg {
+public:
+ NativeRegisterContextDBReg_arm()
+ : NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {}
+
+private:
+ uint32_t GetWatchpointSize(uint32_t wp_index) override;
+
+ std::optional<WatchpointDetails>
+ AdjustWatchpoint(const WatchpointDetails &details) override;
+
+ BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) override;
+
+ uint32_t MakeBreakControlValue(size_t size) override;
+
+ uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override;
+
+ bool ValidateBreakpoint(size_t size,
+ [[maybe_unused]] lldb::addr_t addr) override {
+ // Break on 4 or 2 byte instructions.
+ return size == 4 || size == 2;
+ }
+};
+
+} // namespace lldb_private
+
+#endif // #ifndef lldb_NativeRegisterContextDBReg_arm_h
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
index fbf128553fd5..3b8d6a84c964 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp
@@ -79,7 +79,7 @@ static lldb_private::RegisterInfo g_register_infos_mte[] = {
DEFINE_EXTENSION_REG(mte_ctrl)};
static lldb_private::RegisterInfo g_register_infos_tls[] = {
- DEFINE_EXTENSION_REG(tpidr),
+ DEFINE_EXTENSION_REG_GENERIC(tpidr, LLDB_REGNUM_GENERIC_TP),
// Only present when SME is present
DEFINE_EXTENSION_REG(tpidr2)};
diff --git a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
index c9c4d7ceae55..829fa076d221 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfos_arm64.h
@@ -456,6 +456,7 @@ static uint32_t g_d29_invalidates[] = {fpu_v29, fpu_s29, LLDB_INVALID_REGNUM};
static uint32_t g_d30_invalidates[] = {fpu_v30, fpu_s30, LLDB_INVALID_REGNUM};
static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
+// clang-format off
// Generates register kinds array with DWARF, EH frame and generic kind
#define MISC_KIND(reg, type, generic_kind) \
{ \
@@ -470,6 +471,11 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
LLDB_INVALID_REGNUM, lldb_kind \
}
+#define GENERIC_KIND(genenric_kind) \
+ { \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, genenric_kind, \
+ LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM \
+ }
// Generates register kinds array for registers with only lldb kind
#define KIND_ALL_INVALID \
{ \
@@ -484,8 +490,6 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
#define MISC_FPU_KIND(lldb_kind) LLDB_KIND(lldb_kind)
#define MISC_EXC_KIND(lldb_kind) LLDB_KIND(lldb_kind)
-// clang-format off
-
// Defines a 64-bit general purpose register
#define DEFINE_GPR64(reg, generic_kind) \
{ \
@@ -540,6 +544,12 @@ static uint32_t g_d31_invalidates[] = {fpu_v31, fpu_s31, LLDB_INVALID_REGNUM};
#reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \
KIND_ALL_INVALID, nullptr, nullptr, nullptr, \
}
+
+#define DEFINE_EXTENSION_REG_GENERIC(reg, generic_kind) \
+ { \
+ #reg, nullptr, 8, 0, lldb::eEncodingUint, lldb::eFormatHex, \
+ GENERIC_KIND(generic_kind), nullptr, nullptr, nullptr, \
+ }
static lldb_private::RegisterInfo g_register_infos_arm64_le[] = {
// DEFINE_GPR64(name, GENERIC KIND)
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 88eeddf17878..8f5f1242116f 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -99,7 +99,7 @@ bool ProcessElfCore::CanDebug(lldb::TargetSP target_sp,
ProcessElfCore::ProcessElfCore(lldb::TargetSP target_sp,
lldb::ListenerSP listener_sp,
const FileSpec &core_file)
- : PostMortemProcess(target_sp, listener_sp, core_file) {}
+ : PostMortemProcess(target_sp, listener_sp, core_file), m_uuids() {}
// Destructor
ProcessElfCore::~ProcessElfCore() {
@@ -257,12 +257,12 @@ Status ProcessElfCore::DoLoadCore() {
// the main executable using data we found in the core file notes.
lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
if (!exe_module_sp) {
- // The first entry in the NT_FILE might be our executable
if (!m_nt_file_entries.empty()) {
+ llvm::StringRef executable_path = GetMainExecutablePath();
ModuleSpec exe_module_spec;
exe_module_spec.GetArchitecture() = arch;
- exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid;
- exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path,
+ exe_module_spec.GetUUID() = FindModuleUUID(executable_path);
+ exe_module_spec.GetFileSpec().SetFile(executable_path,
FileSpec::Style::native);
if (exe_module_spec.GetFileSpec()) {
exe_module_sp =
@@ -277,21 +277,38 @@ Status ProcessElfCore::DoLoadCore() {
void ProcessElfCore::UpdateBuildIdForNTFileEntries() {
Log *log = GetLog(LLDBLog::Process);
+ m_uuids.clear();
for (NT_FILE_Entry &entry : m_nt_file_entries) {
- entry.uuid = FindBuidIdInCoreMemory(entry.start);
- if (log && entry.uuid.IsValid())
- LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"",
- __FUNCTION__, entry.start, entry.uuid.GetAsString().c_str(),
- entry.path.c_str());
+ UUID uuid = FindBuidIdInCoreMemory(entry.start);
+ if (uuid.IsValid()) {
+ // Assert that either the path is not in the map or the UUID matches
+ assert(m_uuids.count(entry.path) == 0 || m_uuids[entry.path] == uuid);
+ m_uuids[entry.path] = uuid;
+ if (log)
+ LLDB_LOGF(log, "%s found UUID @ %16.16" PRIx64 ": %s \"%s\"",
+ __FUNCTION__, entry.start, uuid.GetAsString().c_str(),
+ entry.path.c_str());
+ }
}
}
+llvm::StringRef ProcessElfCore::GetMainExecutablePath() {
+ if (m_nt_file_entries.empty())
+ return "";
+
+ // The first entry in the NT_FILE might be our executable
+ llvm::StringRef executable_path = m_nt_file_entries[0].path;
+ // Prefer the NT_FILE entry matching m_executable_name as main executable.
+ for (const NT_FILE_Entry &file_entry : m_nt_file_entries)
+ if (llvm::StringRef(file_entry.path).ends_with("/" + m_executable_name)) {
+ executable_path = file_entry.path;
+ break;
+ }
+ return executable_path;
+}
+
UUID ProcessElfCore::FindModuleUUID(const llvm::StringRef path) {
- // Returns the gnu uuid from matched NT_FILE entry
- for (NT_FILE_Entry &entry : m_nt_file_entries)
- if (path == entry.path && entry.uuid.IsValid())
- return entry.uuid;
- return UUID();
+ return m_uuids[std::string(path)];
}
lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() {
@@ -935,6 +952,7 @@ llvm::Error ProcessElfCore::parseLinuxNotes(llvm::ArrayRef<CoreNote> notes) {
return status.ToError();
thread_data.name.assign (prpsinfo.pr_fname, strnlen (prpsinfo.pr_fname, sizeof (prpsinfo.pr_fname)));
SetID(prpsinfo.pr_pid);
+ m_executable_name = prpsinfo.pr_fname;
break;
}
case ELF::NT_SIGINFO: {
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
index a91c04a277f6..576c6858477a 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -17,6 +17,7 @@
#define LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_PROCESSELFCORE_H
#include <list>
+#include <unordered_map>
#include <vector>
#include "lldb/Target/PostMortemProcess.h"
@@ -115,10 +116,6 @@ private:
lldb::addr_t end;
lldb::addr_t file_ofs;
std::string path;
- // Add a UUID member for convenient access. The UUID value is not in the
- // NT_FILE entries, we will find it in core memory and store it here for
- // easy access.
- lldb_private::UUID uuid;
};
// For ProcessElfCore only
@@ -152,6 +149,12 @@ private:
// NT_FILE entries found from the NOTE segment
std::vector<NT_FILE_Entry> m_nt_file_entries;
+ // Map from file path to UUID for quick lookup
+ std::unordered_map<std::string, lldb_private::UUID> m_uuids;
+
+ // Executable name found from the ELF PRPSINFO
+ std::string m_executable_name;
+
// Parse thread(s) data structures(prstatus, prpsinfo) from given NOTE segment
llvm::Error ParseThreadContextsFromNoteSegment(
const elf::ELFProgramHeader &segment_header,
@@ -165,6 +168,9 @@ private:
lldb_private::UUID FindModuleUUID(const llvm::StringRef path) override;
+ // Returns the main executable path
+ llvm::StringRef GetMainExecutablePath();
+
// Returns the value of certain type of note of a given start address
lldb_private::UUID FindBuidIdInCoreMemory(lldb::addr_t address);
diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
index 3a62081827c6..c9e90ebf8a4d 100644
--- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
+++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.cpp
@@ -23,6 +23,10 @@ RegisterContextCorePOSIX_arm::RegisterContextCorePOSIX_arm(
gpregset.GetByteSize());
m_gpr.SetData(m_gpr_buffer);
m_gpr.SetByteOrder(gpregset.GetByteOrder());
+
+ const llvm::Triple &target_triple =
+ m_register_info_up->GetTargetArchitecture().GetTriple();
+ m_fpr = getRegset(notes, target_triple, ARM_VFP_Desc);
}
RegisterContextCorePOSIX_arm::~RegisterContextCorePOSIX_arm() = default;
@@ -43,14 +47,25 @@ bool RegisterContextCorePOSIX_arm::WriteFPR() {
bool RegisterContextCorePOSIX_arm::ReadRegister(const RegisterInfo *reg_info,
RegisterValue &value) {
- lldb::offset_t offset = reg_info->byte_offset;
- if (offset + reg_info->byte_size <= GetGPRSize()) {
- uint64_t v = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
- if (offset == reg_info->byte_offset + reg_info->byte_size) {
- value = v;
- return true;
+ const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
+ if (reg == LLDB_INVALID_REGNUM)
+ return false;
+
+ if (IsGPR(reg)) {
+ lldb::offset_t offset = reg_info->byte_offset;
+ if (m_gpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size)) {
+ value = m_gpr.GetMaxU64(&offset, reg_info->byte_size);
+ return offset == reg_info->byte_offset + reg_info->byte_size;
}
+ } else if (IsFPR(reg)) {
+ assert(reg_info->byte_offset >= GetGPRSize());
+ lldb::offset_t offset = reg_info->byte_offset - GetGPRSize();
+ if (m_fpr.ValidOffsetForDataOfSize(offset, reg_info->byte_size))
+ return value
+ .SetValueFromData(*reg_info, m_fpr, offset, /*partial_data_ok=*/false)
+ .Success();
}
+
return false;
}
diff --git a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
index 8d773a046bca..e466ee242181 100644
--- a/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
+++ b/lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm.h
@@ -48,6 +48,7 @@ protected:
private:
lldb::DataBufferSP m_gpr_buffer;
lldb_private::DataExtractor m_gpr;
+ lldb_private::DataExtractor m_fpr;
};
#endif // LLDB_SOURCE_PLUGINS_PROCESS_ELF_CORE_REGISTERCONTEXTPOSIXCORE_ARM_H
diff --git a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
index 59382a12cde0..a5b0d788a190 100644
--- a/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
+++ b/lldb/source/Plugins/Process/elf-core/RegisterUtilities.h
@@ -152,6 +152,11 @@ constexpr RegsetDesc AARCH64_GCS_Desc[] = {
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_GCS},
};
+constexpr RegsetDesc ARM_VFP_Desc[] = {
+ {llvm::Triple::FreeBSD, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP},
+ {llvm::Triple::Linux, llvm::Triple::arm, llvm::ELF::NT_ARM_VFP},
+};
+
constexpr RegsetDesc PPC_VMX_Desc[] = {
{llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},
{llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 85e141d41747..91f3a6ce383b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -5773,7 +5773,7 @@ public:
CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter)
: CommandObjectRaw(interpreter, "process plugin packet monitor",
"Send a qRcmd packet through the GDB remote protocol "
- "and print the response."
+ "and print the response. "
"The argument passed to this command will be hex "
"encoded into a valid 'qRcmd' packet, sent and the "
"response will be printed.") {}
diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
index 6037c8d2514b..a780b3f59ade 100644
--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
@@ -799,6 +799,23 @@ Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr,
region_info.SetMapped(MemoryRegionInfo::eNo);
}
return Status();
+ } else {
+ // The corefile has no LC_SEGMENT at this virtual address,
+ // but see if there is a binary whose Section has been
+ // loaded at that address in the current Target.
+ Address addr;
+ if (GetTarget().ResolveLoadAddress(load_addr, addr)) {
+ SectionSP section_sp(addr.GetSection());
+ if (section_sp) {
+ region_info.GetRange().SetRangeBase(
+ section_sp->GetLoadBaseAddress(&GetTarget()));
+ region_info.GetRange().SetByteSize(section_sp->GetByteSize());
+ if (region_info.GetRange().Contains(load_addr)) {
+ region_info.SetLLDBPermissions(section_sp->GetPermissions());
+ return Status();
+ }
+ }
+ }
}
region_info.GetRange().SetRangeBase(load_addr);
diff --git a/lldb/source/Plugins/Process/scripted/CMakeLists.txt b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
index 590166591a41..1516ad3132e3 100644
--- a/lldb/source/Plugins/Process/scripted/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/scripted/CMakeLists.txt
@@ -1,6 +1,7 @@
add_lldb_library(lldbPluginScriptedProcess PLUGIN
ScriptedProcess.cpp
ScriptedThread.cpp
+ ScriptedFrame.cpp
LINK_COMPONENTS
BinaryFormat
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
new file mode 100644
index 000000000000..6519df9185df
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.cpp
@@ -0,0 +1,191 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ScriptedFrame.h"
+
+#include "lldb/Utility/DataBufferHeap.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+void ScriptedFrame::CheckInterpreterAndScriptObject() const {
+ lldbassert(m_script_object_sp && "Invalid Script Object.");
+ lldbassert(GetInterface() && "Invalid Scripted Frame Interface.");
+}
+
+llvm::Expected<std::shared_ptr<ScriptedFrame>>
+ScriptedFrame::Create(ScriptedThread &thread,
+ StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_object) {
+ if (!thread.IsValid())
+ return llvm::createStringError("Invalid scripted thread.");
+
+ thread.CheckInterpreterAndScriptObject();
+
+ auto scripted_frame_interface =
+ thread.GetInterface()->CreateScriptedFrameInterface();
+ if (!scripted_frame_interface)
+ return llvm::createStringError("failed to create scripted frame interface");
+
+ llvm::StringRef frame_class_name;
+ if (!script_object) {
+ std::optional<std::string> class_name =
+ thread.GetInterface()->GetScriptedFramePluginName();
+ if (!class_name || class_name->empty())
+ return llvm::createStringError(
+ "failed to get scripted thread class name");
+ frame_class_name = *class_name;
+ }
+
+ ExecutionContext exe_ctx(thread);
+ auto obj_or_err = scripted_frame_interface->CreatePluginObject(
+ frame_class_name, exe_ctx, args_sp, script_object);
+
+ if (!obj_or_err)
+ return llvm::createStringError(
+ "failed to create script object: %s",
+ llvm::toString(obj_or_err.takeError()).c_str());
+
+ StructuredData::GenericSP owned_script_object_sp = *obj_or_err;
+
+ if (!owned_script_object_sp->IsValid())
+ return llvm::createStringError("created script object is invalid");
+
+ lldb::user_id_t frame_id = scripted_frame_interface->GetID();
+
+ lldb::addr_t pc = scripted_frame_interface->GetPC();
+ SymbolContext sc;
+ Address symbol_addr;
+ if (pc != LLDB_INVALID_ADDRESS) {
+ symbol_addr.SetLoadAddress(pc, &thread.GetProcess()->GetTarget());
+ symbol_addr.CalculateSymbolContext(&sc);
+ }
+
+ std::optional<SymbolContext> maybe_sym_ctx =
+ scripted_frame_interface->GetSymbolContext();
+ if (maybe_sym_ctx) {
+ sc = *maybe_sym_ctx;
+ }
+
+ StructuredData::DictionarySP reg_info =
+ scripted_frame_interface->GetRegisterInfo();
+
+ if (!reg_info)
+ return llvm::createStringError(
+ "failed to get scripted thread registers info");
+
+ std::shared_ptr<DynamicRegisterInfo> register_info_sp =
+ DynamicRegisterInfo::Create(
+ *reg_info, thread.GetProcess()->GetTarget().GetArchitecture());
+
+ lldb::RegisterContextSP reg_ctx_sp;
+
+ std::optional<std::string> reg_data =
+ scripted_frame_interface->GetRegisterContext();
+ if (reg_data) {
+ DataBufferSP data_sp(
+ std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
+
+ if (!data_sp->GetByteSize())
+ return llvm::createStringError("failed to copy raw registers data");
+
+ std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
+ std::make_shared<RegisterContextMemory>(
+ thread, frame_id, *register_info_sp, LLDB_INVALID_ADDRESS);
+ if (!reg_ctx_memory)
+ return llvm::createStringError("failed to create a register context.");
+
+ reg_ctx_memory->SetAllRegisterData(data_sp);
+ reg_ctx_sp = reg_ctx_memory;
+ }
+
+ return std::make_shared<ScriptedFrame>(
+ thread, scripted_frame_interface, frame_id, pc, sc, reg_ctx_sp,
+ register_info_sp, owned_script_object_sp);
+}
+
+ScriptedFrame::ScriptedFrame(ScriptedThread &thread,
+ ScriptedFrameInterfaceSP interface_sp,
+ lldb::user_id_t id, lldb::addr_t pc,
+ SymbolContext &sym_ctx,
+ lldb::RegisterContextSP reg_ctx_sp,
+ std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+ StructuredData::GenericSP script_object_sp)
+ : StackFrame(thread.shared_from_this(), /*frame_idx=*/id,
+ /*concrete_frame_idx=*/id, /*reg_context_sp=*/reg_ctx_sp,
+ /*cfa=*/0, /*pc=*/pc,
+ /*behaves_like_zeroth_frame=*/!id, /*symbol_ctx=*/&sym_ctx),
+ m_scripted_frame_interface_sp(interface_sp),
+ m_script_object_sp(script_object_sp), m_register_info_sp(reg_info_sp) {}
+
+ScriptedFrame::~ScriptedFrame() {}
+
+const char *ScriptedFrame::GetFunctionName() {
+ CheckInterpreterAndScriptObject();
+ std::optional<std::string> function_name = GetInterface()->GetFunctionName();
+ if (!function_name)
+ return nullptr;
+ return ConstString(function_name->c_str()).AsCString();
+}
+
+const char *ScriptedFrame::GetDisplayFunctionName() {
+ CheckInterpreterAndScriptObject();
+ std::optional<std::string> function_name =
+ GetInterface()->GetDisplayFunctionName();
+ if (!function_name)
+ return nullptr;
+ return ConstString(function_name->c_str()).AsCString();
+}
+
+bool ScriptedFrame::IsInlined() { return GetInterface()->IsInlined(); }
+
+bool ScriptedFrame::IsArtificial() const {
+ return GetInterface()->IsArtificial();
+}
+
+bool ScriptedFrame::IsHidden() { return GetInterface()->IsHidden(); }
+
+lldb::ScriptedFrameInterfaceSP ScriptedFrame::GetInterface() const {
+ return m_scripted_frame_interface_sp;
+}
+
+std::shared_ptr<DynamicRegisterInfo> ScriptedFrame::GetDynamicRegisterInfo() {
+ CheckInterpreterAndScriptObject();
+
+ if (!m_register_info_sp) {
+ StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
+
+ Status error;
+ if (!reg_info)
+ return ScriptedInterface::ErrorWithMessage<
+ std::shared_ptr<DynamicRegisterInfo>>(
+ LLVM_PRETTY_FUNCTION, "Failed to get scripted frame registers info.",
+ error, LLDBLog::Thread);
+
+ ThreadSP thread_sp = m_thread_wp.lock();
+ if (!thread_sp || !thread_sp->IsValid())
+ return ScriptedInterface::ErrorWithMessage<
+ std::shared_ptr<DynamicRegisterInfo>>(
+ LLVM_PRETTY_FUNCTION,
+ "Failed to get scripted frame registers info: invalid thread.", error,
+ LLDBLog::Thread);
+
+ ProcessSP process_sp = thread_sp->GetProcess();
+ if (!process_sp || !process_sp->IsValid())
+ return ScriptedInterface::ErrorWithMessage<
+ std::shared_ptr<DynamicRegisterInfo>>(
+ LLVM_PRETTY_FUNCTION,
+ "Failed to get scripted frame registers info: invalid process.",
+ error, LLDBLog::Thread);
+
+ m_register_info_sp = DynamicRegisterInfo::Create(
+ *reg_info, process_sp->GetTarget().GetArchitecture());
+ }
+
+ return m_register_info_sp;
+}
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedFrame.h b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
new file mode 100644
index 000000000000..6e01e2fd7653
--- /dev/null
+++ b/lldb/source/Plugins/Process/scripted/ScriptedFrame.h
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
+#define LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
+
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "ScriptedThread.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
+#include "lldb/Target/StackFrame.h"
+#include <string>
+
+namespace lldb_private {
+class ScriptedThread;
+}
+
+namespace lldb_private {
+
+class ScriptedFrame : public lldb_private::StackFrame {
+
+public:
+ ScriptedFrame(ScriptedThread &thread,
+ lldb::ScriptedFrameInterfaceSP interface_sp,
+ lldb::user_id_t frame_idx, lldb::addr_t pc,
+ SymbolContext &sym_ctx, lldb::RegisterContextSP reg_ctx_sp,
+ std::shared_ptr<DynamicRegisterInfo> reg_info_sp,
+ StructuredData::GenericSP script_object_sp = nullptr);
+
+ ~ScriptedFrame() override;
+
+ static llvm::Expected<std::shared_ptr<ScriptedFrame>>
+ Create(ScriptedThread &thread, StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_object = nullptr);
+
+ bool IsInlined() override;
+ bool IsArtificial() const override;
+ bool IsHidden() override;
+ const char *GetFunctionName() override;
+ const char *GetDisplayFunctionName() override;
+
+private:
+ void CheckInterpreterAndScriptObject() const;
+ lldb::ScriptedFrameInterfaceSP GetInterface() const;
+
+ ScriptedFrame(const ScriptedFrame &) = delete;
+ const ScriptedFrame &operator=(const ScriptedFrame &) = delete;
+
+ std::shared_ptr<DynamicRegisterInfo> GetDynamicRegisterInfo();
+
+ lldb::ScriptedFrameInterfaceSP m_scripted_frame_interface_sp;
+ lldb_private::StructuredData::GenericSP m_script_object_sp;
+ std::shared_ptr<DynamicRegisterInfo> m_register_info_sp;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SCRIPTED_FRAME_H
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
index d0d1508e8517..491efac5aade 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "ScriptedThread.h"
+#include "ScriptedFrame.h"
#include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
#include "Plugins/Process/Utility/StopInfoMachException.h"
@@ -173,40 +174,101 @@ bool ScriptedThread::LoadArtificialStackFrames() {
.str(),
error, LLDBLog::Thread);
- StackFrameListSP frames = GetStackFrameList();
-
- for (size_t idx = 0; idx < arr_size; idx++) {
+ auto create_frame_from_dict =
+ [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+ Status error;
std::optional<StructuredData::Dictionary *> maybe_dict =
arr_sp->GetItemAtIndexAsDictionary(idx);
- if (!maybe_dict)
- return ScriptedInterface::ErrorWithMessage<bool>(
+ if (!maybe_dict) {
+ ScriptedInterface::ErrorWithMessage<bool>(
LLVM_PRETTY_FUNCTION,
llvm::Twine(
"Couldn't get artificial stackframe dictionary at index (" +
llvm::Twine(idx) + llvm::Twine(") from stackframe array."))
.str(),
error, LLDBLog::Thread);
+ return error.ToError();
+ }
StructuredData::Dictionary *dict = *maybe_dict;
lldb::addr_t pc;
- if (!dict->GetValueForKeyAsInteger("pc", pc))
- return ScriptedInterface::ErrorWithMessage<bool>(
+ if (!dict->GetValueForKeyAsInteger("pc", pc)) {
+ ScriptedInterface::ErrorWithMessage<bool>(
LLVM_PRETTY_FUNCTION,
"Couldn't find value for key 'pc' in stackframe dictionary.", error,
LLDBLog::Thread);
+ return error.ToError();
+ }
Address symbol_addr;
symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
bool cfa_is_valid = false;
+ const bool artificial = false;
const bool behaves_like_zeroth_frame = false;
SymbolContext sc;
symbol_addr.CalculateSymbolContext(&sc);
- StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
- this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
- StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc);
+ return std::make_shared<StackFrame>(this->shared_from_this(), idx, idx, cfa,
+ cfa_is_valid, pc,
+ StackFrame::Kind::Synthetic, artificial,
+ behaves_like_zeroth_frame, &sc);
+ };
+
+ auto create_frame_from_script_object =
+ [this, arr_sp](size_t idx) -> llvm::Expected<StackFrameSP> {
+ Status error;
+ StructuredData::ObjectSP object_sp = arr_sp->GetItemAtIndex(idx);
+ if (!object_sp || !object_sp->GetAsGeneric()) {
+ ScriptedInterface::ErrorWithMessage<bool>(
+ LLVM_PRETTY_FUNCTION,
+ llvm::Twine("Couldn't get artificial stackframe object at index (" +
+ llvm::Twine(idx) +
+ llvm::Twine(") from stackframe array."))
+ .str(),
+ error, LLDBLog::Thread);
+ return error.ToError();
+ }
+
+ auto frame_or_error =
+ ScriptedFrame::Create(*this, nullptr, object_sp->GetAsGeneric());
+
+ if (!frame_or_error) {
+ ScriptedInterface::ErrorWithMessage<bool>(
+ LLVM_PRETTY_FUNCTION, toString(frame_or_error.takeError()), error);
+ return error.ToError();
+ }
+
+ StackFrameSP frame_sp = frame_or_error.get();
+ lldbassert(frame_sp && "Couldn't initialize scripted frame.");
+
+ return frame_sp;
+ };
+
+ StackFrameListSP frames = GetStackFrameList();
+
+ for (size_t idx = 0; idx < arr_size; idx++) {
+ StackFrameSP synth_frame_sp = nullptr;
+
+ auto frame_from_dict_or_err = create_frame_from_dict(idx);
+ if (!frame_from_dict_or_err) {
+ auto frame_from_script_obj_or_err = create_frame_from_script_object(idx);
+
+ if (!frame_from_script_obj_or_err) {
+ return ScriptedInterface::ErrorWithMessage<bool>(
+ LLVM_PRETTY_FUNCTION,
+ llvm::Twine("Couldn't add artificial frame (" + llvm::Twine(idx) +
+ llvm::Twine(") to ScriptedThread StackFrameList."))
+ .str(),
+ error, LLDBLog::Thread);
+ } else {
+ llvm::consumeError(frame_from_dict_or_err.takeError());
+ synth_frame_sp = *frame_from_script_obj_or_err;
+ }
+ } else {
+ synth_frame_sp = *frame_from_dict_or_err;
+ }
if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
return ScriptedInterface::ErrorWithMessage<bool>(
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedThread.h b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
index cd224d60ceef..ee5ace405967 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedThread.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedThread.h
@@ -15,11 +15,12 @@
#include "Plugins/Process/Utility/RegisterContextMemory.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
-#include "lldb/Target//DynamicRegisterInfo.h"
+#include "lldb/Target/DynamicRegisterInfo.h"
#include "lldb/Target/Thread.h"
namespace lldb_private {
class ScriptedProcess;
+class ScriptedFrame;
}
namespace lldb_private {
@@ -61,6 +62,8 @@ public:
StructuredData::ObjectSP FetchThreadExtendedInfo() override;
private:
+ friend class ScriptedFrame;
+
void CheckInterpreterAndScriptObject() const;
lldb::ScriptedThreadInterfaceSP GetInterface() const;
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index c359663239dc..dc18c8e06803 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -10,14 +10,15 @@
#include "Resource.h"
#include "Tool.h"
#include "lldb/Core/PluginManager.h"
-#include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Protocol/MCP/Server.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/Threading.h"
#include <thread>
-#include <variant>
using namespace lldb_private;
using namespace lldb_private::mcp;
@@ -26,24 +27,10 @@ using namespace llvm;
LLDB_PLUGIN_DEFINE(ProtocolServerMCP)
-static constexpr size_t kChunkSize = 1024;
static constexpr llvm::StringLiteral kName = "lldb-mcp";
static constexpr llvm::StringLiteral kVersion = "0.1.0";
-ProtocolServerMCP::ProtocolServerMCP()
- : ProtocolServer(),
- lldb_protocol::mcp::Server(std::string(kName), std::string(kVersion)) {
- AddNotificationHandler("notifications/initialized",
- [](const lldb_protocol::mcp::Notification &) {
- LLDB_LOG(GetLog(LLDBLog::Host),
- "MCP initialization complete");
- });
-
- AddTool(
- std::make_unique<CommandTool>("lldb_command", "Run an lldb command."));
-
- AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
-}
+ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {}
ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
@@ -53,6 +40,8 @@ void ProtocolServerMCP::Initialize() {
}
void ProtocolServerMCP::Terminate() {
+ if (llvm::Error error = ProtocolServer::Terminate())
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}");
PluginManager::UnregisterPlugin(CreateInstance);
}
@@ -64,57 +53,37 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
return "MCP Server.";
}
+void ProtocolServerMCP::Extend(lldb_protocol::mcp::Server &server) const {
+ server.AddNotificationHandler("notifications/initialized",
+ [](const lldb_protocol::mcp::Notification &) {
+ LLDB_LOG(GetLog(LLDBLog::Host),
+ "MCP initialization complete");
+ });
+ server.AddTool(
+ std::make_unique<CommandTool>("lldb_command", "Run an lldb command."));
+ server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
+}
+
void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
- LLDB_LOG(GetLog(LLDBLog::Host), "New MCP client ({0}) connected",
- m_clients.size() + 1);
+ Log *log = GetLog(LLDBLog::Host);
+ std::string client_name = llvm::formatv("client_{0}", m_instances.size() + 1);
+ LLDB_LOG(log, "New MCP client connected: {0}", client_name);
lldb::IOObjectSP io_sp = std::move(socket);
- auto client_up = std::make_unique<Client>();
- client_up->io_sp = io_sp;
- Client *client = client_up.get();
-
- Status status;
- auto read_handle_up = m_loop.RegisterReadObject(
- io_sp,
- [this, client](MainLoopBase &loop) {
- if (llvm::Error error = ReadCallback(*client)) {
- LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}");
- client->read_handle_up.reset();
- }
- },
- status);
- if (status.Fail())
+ auto transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
+ io_sp, io_sp, [client_name](llvm::StringRef message) {
+ LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message);
+ });
+ auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
+ std::string(kName), std::string(kVersion), std::move(transport_up),
+ m_loop);
+ Extend(*instance_up);
+ llvm::Error error = instance_up->Run();
+ if (error) {
+ LLDB_LOG_ERROR(log, std::move(error), "Failed to run MCP server: {0}");
return;
-
- client_up->read_handle_up = std::move(read_handle_up);
- m_clients.emplace_back(std::move(client_up));
-}
-
-llvm::Error ProtocolServerMCP::ReadCallback(Client &client) {
- char chunk[kChunkSize];
- size_t bytes_read = sizeof(chunk);
- if (Status status = client.io_sp->Read(chunk, bytes_read); status.Fail())
- return status.takeError();
- client.buffer.append(chunk, bytes_read);
-
- for (std::string::size_type pos;
- (pos = client.buffer.find('\n')) != std::string::npos;) {
- llvm::Expected<std::optional<lldb_protocol::mcp::Message>> message =
- HandleData(StringRef(client.buffer.data(), pos));
- client.buffer = client.buffer.erase(0, pos + 1);
- if (!message)
- return message.takeError();
-
- if (*message) {
- std::string Output;
- llvm::raw_string_ostream OS(Output);
- OS << llvm::formatv("{0}", toJSON(**message)) << '\n';
- size_t num_bytes = Output.size();
- return client.io_sp->Write(Output.data(), num_bytes).takeError();
- }
}
-
- return llvm::Error::success();
+ m_instances.push_back(std::move(instance_up));
}
llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
@@ -138,7 +107,19 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
if (llvm::Error error = handles.takeError())
return error;
+ auto listening_uris = m_listener->GetListeningConnectionURI();
+ if (listening_uris.empty())
+ return createStringError("failed to get listening connections");
+ std::string address =
+ llvm::join(m_listener->GetListeningConnectionURI(), ", ");
+
+ ServerInfo info{listening_uris[0]};
+ llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
+ if (!handle)
+ return handle.takeError();
+
m_running = true;
+ m_server_info_handle = std::move(*handle);
m_listen_handlers = std::move(*handles);
m_loop_thread = std::thread([=] {
llvm::set_thread_name("protocol-server.mcp");
@@ -158,27 +139,15 @@ llvm::Error ProtocolServerMCP::Stop() {
// Stop the main loop.
m_loop.AddPendingCallback(
- [](MainLoopBase &loop) { loop.RequestTermination(); });
+ [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
// Wait for the main loop to exit.
if (m_loop_thread.joinable())
m_loop_thread.join();
- {
- std::lock_guard<std::mutex> guard(m_mutex);
- m_listener.reset();
- m_listen_handlers.clear();
- m_clients.clear();
- }
+ m_listen_handlers.clear();
+ m_server_info_handle = ServerInfoHandle();
+ m_instances.clear();
return llvm::Error::success();
}
-
-lldb_protocol::mcp::Capabilities ProtocolServerMCP::GetCapabilities() {
- lldb_protocol::mcp::Capabilities capabilities;
- capabilities.tools.listChanged = true;
- // FIXME: Support sending notifications when a debugger/target are
- // added/removed.
- capabilities.resources.listChanged = false;
- return capabilities;
-}
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index 7fe909a728b8..0251664a2acc 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -18,8 +18,7 @@
namespace lldb_private::mcp {
-class ProtocolServerMCP : public ProtocolServer,
- public lldb_protocol::mcp::Server {
+class ProtocolServerMCP : public ProtocolServer {
public:
ProtocolServerMCP();
virtual ~ProtocolServerMCP() override;
@@ -39,26 +38,25 @@ public:
Socket *GetSocket() const override { return m_listener.get(); }
+protected:
+ // This adds tools and resource providers that
+ // are specific to this server. Overridable by the unit tests.
+ virtual void Extend(lldb_protocol::mcp::Server &server) const;
+
private:
void AcceptCallback(std::unique_ptr<Socket> socket);
- lldb_protocol::mcp::Capabilities GetCapabilities() override;
-
bool m_running = false;
- MainLoop m_loop;
+ lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
+ lldb_private::MainLoop m_loop;
std::thread m_loop_thread;
+ std::mutex m_mutex;
std::unique_ptr<Socket> m_listener;
- std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers;
- struct Client {
- lldb::IOObjectSP io_sp;
- MainLoopBase::ReadHandleUP read_handle_up;
- std::string buffer;
- };
- llvm::Error ReadCallback(Client &client);
- std::vector<std::unique_ptr<Client>> m_clients;
+ std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers;
+ std::vector<std::unique_ptr<lldb_protocol::mcp::Server>> m_instances;
};
} // namespace lldb_private::mcp
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
index e94d2cdd65e0..581424510d4c 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp
@@ -8,7 +8,6 @@
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Target/Platform.h"
using namespace lldb_private;
using namespace lldb_private::mcp;
@@ -124,7 +123,7 @@ DebuggerResourceProvider::GetResources() const {
return resources;
}
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
auto [protocol, path] = uri.split("://");
@@ -161,7 +160,7 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const {
return ReadDebuggerResource(uri, debugger_idx);
}
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
lldb::user_id_t debugger_id) {
lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id);
@@ -173,17 +172,17 @@ DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri,
debugger_resource.name = debugger_sp->GetInstanceName();
debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets();
- lldb_protocol::mcp::ResourceContents contents;
+ lldb_protocol::mcp::TextResourceContents contents;
contents.uri = uri;
contents.mimeType = kMimeTypeJSON;
contents.text = llvm::formatv("{0}", toJSON(debugger_resource));
- lldb_protocol::mcp::ResourceResult result;
+ lldb_protocol::mcp::ReadResourceResult result;
result.contents.push_back(contents);
return result;
}
-llvm::Expected<lldb_protocol::mcp::ResourceResult>
+llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
lldb::user_id_t debugger_id,
size_t target_idx) {
@@ -209,12 +208,12 @@ DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri,
if (lldb::PlatformSP platform_sp = target_sp->GetPlatform())
target_resource.platform = platform_sp->GetName();
- lldb_protocol::mcp::ResourceContents contents;
+ lldb_protocol::mcp::TextResourceContents contents;
contents.uri = uri;
contents.mimeType = kMimeTypeJSON;
contents.text = llvm::formatv("{0}", toJSON(target_resource));
- lldb_protocol::mcp::ResourceResult result;
+ lldb_protocol::mcp::ReadResourceResult result;
result.contents.push_back(contents);
return result;
}
diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.h b/lldb/source/Plugins/Protocol/MCP/Resource.h
index e2382a74f796..0c6576602905 100644
--- a/lldb/source/Plugins/Protocol/MCP/Resource.h
+++ b/lldb/source/Plugins/Protocol/MCP/Resource.h
@@ -11,7 +11,11 @@
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Resource.h"
-#include "lldb/lldb-private.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <cstddef>
#include <vector>
namespace lldb_private::mcp {
@@ -21,9 +25,8 @@ public:
using ResourceProvider::ResourceProvider;
virtual ~DebuggerResourceProvider() = default;
- virtual std::vector<lldb_protocol::mcp::Resource>
- GetResources() const override;
- virtual llvm::Expected<lldb_protocol::mcp::ResourceResult>
+ std::vector<lldb_protocol::mcp::Resource> GetResources() const override;
+ llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
ReadResource(llvm::StringRef uri) const override;
private:
@@ -31,9 +34,9 @@ private:
static lldb_protocol::mcp::Resource GetTargetResource(size_t target_idx,
Target &target);
- static llvm::Expected<lldb_protocol::mcp::ResourceResult>
+ static llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id);
- static llvm::Expected<lldb_protocol::mcp::ResourceResult>
+ static llvm::Expected<lldb_protocol::mcp::ReadResourceResult>
ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id,
size_t target_idx);
};
diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.cpp b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
index 143470702a6f..2f451bf76e81 100644
--- a/lldb/source/Plugins/Protocol/MCP/Tool.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/Tool.cpp
@@ -7,9 +7,9 @@
//===----------------------------------------------------------------------===//
#include "Tool.h"
-#include "lldb/Core/Module.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Protocol/MCP/Protocol.h"
using namespace lldb_private;
using namespace lldb_protocol;
@@ -29,10 +29,10 @@ bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A,
O.mapOptional("arguments", A.arguments);
}
-/// Helper function to create a TextResult from a string output.
-static lldb_protocol::mcp::TextResult createTextResult(std::string output,
- bool is_error = false) {
- lldb_protocol::mcp::TextResult text_result;
+/// Helper function to create a CallToolResult from a string output.
+static lldb_protocol::mcp::CallToolResult
+createTextResult(std::string output, bool is_error = false) {
+ lldb_protocol::mcp::CallToolResult text_result;
text_result.content.emplace_back(
lldb_protocol::mcp::TextContent{{std::move(output)}});
text_result.isError = is_error;
@@ -41,7 +41,7 @@ static lldb_protocol::mcp::TextResult createTextResult(std::string output,
} // namespace
-llvm::Expected<lldb_protocol::mcp::TextResult>
+llvm::Expected<lldb_protocol::mcp::CallToolResult>
CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) {
if (!std::holds_alternative<json::Value>(args))
return createStringError("CommandTool requires arguments");
diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.h b/lldb/source/Plugins/Protocol/MCP/Tool.h
index b7b1756eb38d..1886525b9168 100644
--- a/lldb/source/Plugins/Protocol/MCP/Tool.h
+++ b/lldb/source/Plugins/Protocol/MCP/Tool.h
@@ -9,11 +9,11 @@
#ifndef LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H
#define LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H
-#include "lldb/Core/Debugger.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/Protocol/MCP/Tool.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
-#include <string>
+#include <optional>
namespace lldb_private::mcp {
@@ -22,10 +22,10 @@ public:
using lldb_protocol::mcp::Tool::Tool;
~CommandTool() = default;
- virtual llvm::Expected<lldb_protocol::mcp::TextResult>
+ llvm::Expected<lldb_protocol::mcp::CallToolResult>
Call(const lldb_protocol::mcp::ToolArguments &args) override;
- virtual std::optional<llvm::json::Value> GetSchema() const override;
+ std::optional<llvm::json::Value> GetSchema() const override;
};
} // namespace lldb_private::mcp
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index 04370940423a..09103573b89c 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -22,6 +22,7 @@ endif()
add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
OperatingSystemPythonInterface.cpp
ScriptInterpreterPythonInterfaces.cpp
+ ScriptedFramePythonInterface.cpp
ScriptedPlatformPythonInterface.cpp
ScriptedProcessPythonInterface.cpp
ScriptedPythonInterface.cpp
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
index 02dc06507caf..3814f4661507 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h
@@ -17,6 +17,7 @@
#include "OperatingSystemPythonInterface.h"
#include "ScriptedBreakpointPythonInterface.h"
+#include "ScriptedFramePythonInterface.h"
#include "ScriptedPlatformPythonInterface.h"
#include "ScriptedProcessPythonInterface.h"
#include "ScriptedStopHookPythonInterface.h"
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
new file mode 100644
index 000000000000..20ca7a2c0135
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/lldb-enumerations.h"
+
+#if LLDB_ENABLE_PYTHON
+
+// LLDB Python header must be included first
+#include "../lldb-python.h"
+
+#include "../SWIGPythonBridge.h"
+#include "../ScriptInterpreterPythonImpl.h"
+#include "ScriptedFramePythonInterface.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+ScriptedFramePythonInterface::ScriptedFramePythonInterface(
+ ScriptInterpreterPythonImpl &interpreter)
+ : ScriptedFrameInterface(), ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+ScriptedFramePythonInterface::CreatePluginObject(
+ const llvm::StringRef class_name, ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) {
+ ExecutionContextRefSP exe_ctx_ref_sp =
+ std::make_shared<ExecutionContextRef>(exe_ctx);
+ StructuredDataImpl sd_impl(args_sp);
+ return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj,
+ exe_ctx_ref_sp, sd_impl);
+}
+
+lldb::user_id_t ScriptedFramePythonInterface::GetID() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_id", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return LLDB_INVALID_FRAME_ID;
+
+ return obj->GetUnsignedIntegerValue(LLDB_INVALID_FRAME_ID);
+}
+
+lldb::addr_t ScriptedFramePythonInterface::GetPC() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_pc", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return LLDB_INVALID_ADDRESS;
+
+ return obj->GetUnsignedIntegerValue(LLDB_INVALID_ADDRESS);
+}
+
+std::optional<SymbolContext> ScriptedFramePythonInterface::GetSymbolContext() {
+ Status error;
+ auto sym_ctx = Dispatch<SymbolContext>("get_symbol_context", error);
+
+ if (error.Fail()) {
+ return ErrorWithMessage<SymbolContext>(LLVM_PRETTY_FUNCTION,
+ error.AsCString(), error);
+ }
+
+ return sym_ctx;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetFunctionName() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_function_name", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+
+ return obj->GetStringValue().str();
+}
+
+std::optional<std::string>
+ScriptedFramePythonInterface::GetDisplayFunctionName() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_display_function_name", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+
+ return obj->GetStringValue().str();
+}
+
+bool ScriptedFramePythonInterface::IsInlined() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("is_inlined", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsArtificial() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("is_artificial", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+bool ScriptedFramePythonInterface::IsHidden() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("is_hidden", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+StructuredData::DictionarySP ScriptedFramePythonInterface::GetRegisterInfo() {
+ Status error;
+ StructuredData::DictionarySP dict =
+ Dispatch<StructuredData::DictionarySP>("get_register_info", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, dict,
+ error))
+ return {};
+
+ return dict;
+}
+
+std::optional<std::string> ScriptedFramePythonInterface::GetRegisterContext() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_register_context", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+
+ return obj->GetAsString()->GetValue().str();
+}
+
+#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
new file mode 100644
index 000000000000..3aff237ae65d
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedFramePythonInterface.h
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedPythonInterface.h"
+#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h"
+#include <optional>
+
+namespace lldb_private {
+class ScriptedFramePythonInterface : public ScriptedFrameInterface,
+ public ScriptedPythonInterface {
+public:
+ ScriptedFramePythonInterface(ScriptInterpreterPythonImpl &interpreter);
+
+ llvm::Expected<StructuredData::GenericSP>
+ CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx,
+ StructuredData::DictionarySP args_sp,
+ StructuredData::Generic *script_obj = nullptr) override;
+
+ llvm::SmallVector<AbstractMethodRequirement>
+ GetAbstractMethodRequirements() const override {
+ return llvm::SmallVector<AbstractMethodRequirement>({{"get_id"}});
+ }
+
+ lldb::user_id_t GetID() override;
+
+ lldb::addr_t GetPC() override;
+
+ std::optional<SymbolContext> GetSymbolContext() override;
+
+ std::optional<std::string> GetFunctionName() override;
+
+ std::optional<std::string> GetDisplayFunctionName() override;
+
+ bool IsInlined() override;
+
+ bool IsArtificial() override;
+
+ bool IsHidden() override;
+
+ StructuredData::DictionarySP GetRegisterInfo() override;
+
+ std::optional<std::string> GetRegisterContext() override;
+};
+} // namespace lldb_private
+
+#endif // LLDB_ENABLE_PYTHON
+#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDFRAMEPYTHONINTERFACE_H
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index b49d1d82fe53..8083ccae0402 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -167,7 +167,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
if (!sb_mem_reg_info) {
error = Status::FromErrorStringWithFormat(
- "Couldn't cast lldb::SBMemoryRegionInfo to lldb::MemoryRegionInfoSP.");
+ "Couldn't cast lldb::SBMemoryRegionInfo to "
+ "lldb_private::MemoryRegionInfo.");
return {};
}
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
index 8af89d761764..fd4d231a747f 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.cpp
@@ -144,4 +144,21 @@ StructuredData::ArraySP ScriptedThreadPythonInterface::GetExtendedInfo() {
return arr;
}
+std::optional<std::string>
+ScriptedThreadPythonInterface::GetScriptedFramePluginName() {
+ Status error;
+ StructuredData::ObjectSP obj = Dispatch("get_scripted_frame_plugin", error);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+
+ return obj->GetStringValue().str();
+}
+
+lldb::ScriptedFrameInterfaceSP
+ScriptedThreadPythonInterface::CreateScriptedFrameInterface() {
+ return m_interpreter.CreateScriptedFrameInterface();
+}
+
#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
index 1fb23b39c707..043557a82746 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedThreadPythonInterface.h
@@ -51,6 +51,11 @@ public:
std::optional<std::string> GetRegisterContext() override;
StructuredData::ArraySP GetExtendedInfo() override;
+
+ std::optional<std::string> GetScriptedFramePluginName() override;
+
+protected:
+ lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 9330a634489a..73c5c72932ff 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1521,6 +1521,11 @@ ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
return std::make_shared<ScriptedThreadPythonInterface>(*this);
}
+ScriptedFrameInterfaceSP
+ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() {
+ return std::make_shared<ScriptedFramePythonInterface>(*this);
+}
+
ScriptedThreadPlanInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() {
return std::make_shared<ScriptedThreadPlanPythonInterface>(*this);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
index 83b64b85faeb..dedac280788f 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h
@@ -99,6 +99,8 @@ public:
lldb::ScriptedThreadInterfaceSP CreateScriptedThreadInterface() override;
+ lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() override;
+
lldb::ScriptedThreadPlanInterfaceSP
CreateScriptedThreadPlanInterface() override;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index a429ea848b7f..5ffb4423969c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -252,7 +252,7 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram,
}
static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
- char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+ const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
if (!name)
return {};
@@ -286,7 +286,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
if (die_id == LLDB_INVALID_UID)
return {};
- return FunctionCallLabel{/*module_id=*/module_id,
+ // Note, discriminator is added by Clang during mangling.
+ return FunctionCallLabel{/*discriminator=*/{},
+ /*module_id=*/module_id,
/*symbol_id=*/die_id,
/*.lookup_name=*/name}
.toString();
@@ -2230,6 +2232,18 @@ bool DWARFASTParserClang::CompleteRecordType(const DWARFDIE &die,
for (DelayedAddObjCClassProperty &property : delayed_properties)
property.Finalize();
}
+ } else if (Language::LanguageIsObjC(
+ static_cast<LanguageType>(die.GetAttributeValueAsUnsigned(
+ DW_AT_APPLE_runtime_class, eLanguageTypeUnknown)))) {
+ /// The forward declaration was C++ but the definition is Objective-C.
+ /// We currently don't handle such situations. In such cases, keep the
+ /// forward declaration without a definition to avoid violating Clang AST
+ /// invariants.
+ LLDB_LOG(GetLog(LLDBLog::Expressions),
+ "WARNING: Type completion aborted because forward declaration for "
+ "'{0}' is C++ while definition is Objective-C.",
+ llvm::StringRef(die.GetName()));
+ return {};
}
if (!bases.empty()) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index b15e0c15fedb..d3d0110d5e30 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,11 +7,14 @@
//===----------------------------------------------------------------------===//
#include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/FormatAdapters.h"
#include "llvm/Support/Threading.h"
@@ -23,6 +26,7 @@
#include "lldb/Core/Progress.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/Value.h"
+#include "lldb/Expression/Expression.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/RegularExpression.h"
@@ -79,6 +83,7 @@
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatVariadic.h"
@@ -2484,34 +2489,148 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die,
return false;
}
-DWARFDIE
-SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) {
- DWARFDIE definition;
- Module::LookupInfo info(ConstString(label.lookup_name),
- lldb::eFunctionNameTypeFull,
- lldb::eLanguageTypeUnknown);
-
- m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
- if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
- return IterationAction::Continue;
+static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+ switch (kind) {
+ case clang::CXXCtorType::Ctor_Complete:
+ return "C1";
+ case clang::CXXCtorType::Ctor_Base:
+ return "C2";
+ case clang::CXXCtorType::Ctor_Unified:
+ return "C4";
+ case clang::CXXCtorType::Ctor_CopyingClosure:
+ case clang::CXXCtorType::Ctor_DefaultClosure:
+ case clang::CXXCtorType::Ctor_Comdat:
+ llvm_unreachable("Unexpected constructor kind.");
+ }
+}
- // We don't check whether the specification DIE for this function
- // corresponds to the declaration DIE because the declaration might be in
- // a type-unit but the definition in the compile-unit (and it's
- // specifcation would point to the declaration in the compile-unit). We
- // rely on the mangled name within the module to be enough to find us the
- // unique definition.
- definition = entry;
- return IterationAction::Stop;
- });
+static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+ switch (kind) {
+ case clang::CXXDtorType::Dtor_Deleting:
+ return "D0";
+ case clang::CXXDtorType::Dtor_Complete:
+ return "D1";
+ case clang::CXXDtorType::Dtor_Base:
+ return "D2";
+ case clang::CXXDtorType::Dtor_Unified:
+ return "D4";
+ case clang::CXXDtorType::Dtor_Comdat:
+ llvm_unreachable("Unexpected destructor kind.");
+ }
+}
+
+static llvm::StringRef
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+ const bool is_ctor = discriminator.consume_front("C");
+ if (!is_ctor && !discriminator.consume_front("D"))
+ return {};
+
+ uint64_t structor_kind;
+ if (!llvm::to_integer(discriminator, structor_kind))
+ return {};
+
+ if (is_ctor) {
+ if (structor_kind > clang::CXXCtorType::Ctor_Unified)
+ return {};
+
+ return ClangToItaniumCtorKind(
+ static_cast<clang::CXXCtorType>(structor_kind));
+ }
+
+ if (structor_kind > clang::CXXDtorType::Dtor_Unified)
+ return {};
+
+ return ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+llvm::Expected<DWARFDIE>
+SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+ const DWARFDIE &declaration) {
+ auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE {
+ DWARFDIE found;
+ Module::LookupInfo info(ConstString(lookup_name),
+ lldb::eFunctionNameTypeFull,
+ lldb::eLanguageTypeUnknown);
+
+ m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+ if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+ return IterationAction::Continue;
+
+ found = entry;
+ return IterationAction::Stop;
+ });
+
+ return found;
+ };
+
+ DWARFDIE definition = do_lookup(label.lookup_name);
+ if (definition.IsValid())
+ return definition;
+
+ // This is not a structor lookup. Nothing else to be done here.
+ if (label.discriminator.empty())
+ return llvm::createStringError(
+ "no definition DIE found in this SymbolFile");
+
+ // We're doing a structor lookup. Maybe we didn't find the structor variant
+ // because the complete object structor was aliased to the base object
+ // structor. Try finding the alias instead.
+ //
+ // TODO: there are other reasons for why a subprogram definition might be
+ // missing. Ideally DWARF would tell us more details about which structor
+ // variant a DIE corresponds to and whether it's an alias.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ label.lookup_name);
+ if (!subst_or_err)
+ return subst_or_err.takeError();
+
+ definition = do_lookup(*subst_or_err);
+
+ if (!definition.IsValid())
+ return llvm::createStringError(
+ "failed to find definition DIE for structor alias in fallback lookup");
return definition;
}
llvm::Expected<SymbolContext>
-SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+ if (!label.discriminator.empty()) {
+ llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4";
+
+ llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator);
+ if (variant.empty())
+ return llvm::createStringError(
+ "failed to get Itanium variant for discriminator");
+
+ if (from == variant)
+ return llvm::createStringError(
+ "tried substituting unified structor variant into label");
+
+ // If we failed to substitute unified mangled name, don't try to do a lookup
+ // using the unified name because there may be multiple definitions for it
+ // in the index, and we wouldn't know which one to choose.
+ auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
+ label.lookup_name, from, variant);
+ if (!subst_or_err)
+ return llvm::joinErrors(
+ llvm::createStringError(llvm::formatv(
+ "failed to substitute {0} for {1} in mangled name {2}:", from,
+ variant, label.lookup_name)),
+ subst_or_err.takeError());
+
+ if (!*subst_or_err)
+ return llvm::createStringError(
+ llvm::formatv("got invalid substituted mangled named (substituted "
+ "{0} for {1} in mangled name {2})",
+ from, variant, label.lookup_name));
+
+ label.lookup_name = subst_or_err->GetStringRef();
+ }
+
DWARFDIE die = GetDIE(label.symbol_id);
if (!die.IsValid())
return llvm::createStringError(
@@ -2520,11 +2639,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
// Label was created using a declaration DIE. Need to fetch the definition
// to resolve the function call.
if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
- auto definition = FindFunctionDefinition(label);
- if (!definition)
- return llvm::createStringError("failed to find definition DIE");
+ auto die_or_err = FindFunctionDefinition(label, die);
+ if (!die_or_err)
+ return llvm::joinErrors(
+ llvm::createStringError("failed to find definition DIE:"),
+ die_or_err.takeError());
- die = std::move(definition);
+ die = std::move(*die_or_err);
}
SymbolContextList sc_list;
@@ -4502,9 +4623,8 @@ void SymbolFileDWARF::GetCompileOptions(
}
}
-std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
- uint32_t total_dwo_count = 0;
- uint32_t loaded_dwo_count = 0;
+DWOStats SymbolFileDWARF::GetDwoStats() {
+ DWOStats stats;
DWARFDebugInfo &info = DebugInfo();
const size_t num_cus = info.GetNumUnits();
@@ -4517,16 +4637,21 @@ std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
if (!dwarf_cu->GetDWOId().has_value())
continue;
- total_dwo_count++;
+ stats.dwo_file_count++;
// If we have a DWO symbol file, that means we were able to successfully
// load it.
SymbolFile *dwo_symfile =
dwarf_cu->GetDwoSymbolFile(/*load_all_debug_info=*/false);
if (dwo_symfile) {
- loaded_dwo_count++;
+ stats.loaded_dwo_file_count++;
}
+
+ // Check if this unit has a DWO load error, false by default.
+ const Status &dwo_error = dwarf_cu->GetDwoError();
+ if (dwo_error.Fail())
+ stats.dwo_error_count++;
}
- return {loaded_dwo_count, total_dwo_count};
+ return stats;
}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index d7db8a3c0869..85306d8b4fb5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -283,10 +283,11 @@ public:
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
bool load_all_debug_info = false) override;
- // Gets a pair of loaded and total dwo file counts.
- // For split-dwarf files, this reports the counts for successfully loaded DWO
- // CUs and total DWO CUs. For non-split-dwarf files, this reports 0 for both.
- std::pair<uint32_t, uint32_t> GetDwoFileCounts() override;
+ /// Gets statistics about dwo files associated with this symbol file.
+ /// For split-dwarf files, this reports the counts for successfully loaded DWO
+ /// CUs, total DWO CUs, and the number of DWO CUs with loading errors.
+ /// For non-split-dwarf files, this reports 0 for all.
+ DWOStats GetDwoStats() override;
DWARFContext &GetDWARFContext() { return m_context; }
@@ -378,7 +379,9 @@ private:
/// SymbolFile.
///
/// \returns A valid definition DIE on success.
- DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label);
+ llvm::Expected<DWARFDIE>
+ FindFunctionDefinition(const FunctionCallLabel &label,
+ const DWARFDIE &declaration);
protected:
SymbolFileDWARF(const SymbolFileDWARF &) = delete;
@@ -445,7 +448,7 @@ protected:
DIEArray &&variable_dies);
llvm::Expected<SymbolContext>
- ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+ ResolveFunctionCallLabel(FunctionCallLabel &label) override;
// Given a die_offset, figure out the symbol context representing that die.
bool ResolveFunction(const DWARFDIE &die, bool include_inlines,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 9d7452a1988f..8b8229a7020c 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1603,8 +1603,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
});
}
-llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(
- const FunctionCallLabel &label) {
+llvm::Expected<SymbolContext>
+SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
if (!oso_dwarf)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index e1f1df23951c..bce1ed2671af 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -145,7 +145,7 @@ public:
GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
llvm::Expected<SymbolContext>
- ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+ ResolveFunctionCallLabel(FunctionCallLabel &label) override;
protected:
enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
index 709281cb3270..933c4361d93d 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -1169,6 +1169,7 @@ clang::QualType PdbAstBuilder::CreateEnumType(PdbTypeSymId id,
clang::QualType PdbAstBuilder::CreateArrayType(const ArrayRecord &ar) {
clang::QualType element_type = GetOrCreateType(ar.ElementType);
+ TypeSystemClang::RequireCompleteType(ToCompilerType(element_type));
SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>(
m_clang.GetSymbolFile()->GetBackingSymbolFile());
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 112eb06e462f..e99c585d7eb1 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1624,6 +1624,8 @@ size_t SymbolFileNativePDB::ParseBlocksRecursive(Function &func) {
for (uint64_t uid : remove_uids) {
m_inline_sites.erase(uid);
}
+
+ func.GetBlock(false).SetBlockInfoHasBeenParsed(true, true);
return count;
}
@@ -1655,22 +1657,62 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) {
clang->GetNativePDBParser()->Dump(s, filter);
}
-void SymbolFileNativePDB::CacheFunctionNames() {
- if (!m_func_full_names.IsEmpty())
+void SymbolFileNativePDB::CacheGlobalBaseNames() {
+ if (!m_func_full_names.IsEmpty() || !m_global_variable_base_names.IsEmpty())
return;
// (segment, code offset) -> gid
- std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids;
+ std::map<std::pair<uint16_t, uint32_t>, uint32_t> func_addr_ids;
- // First, find all function references in the globals table.
+ // First, look through all items in the globals table.
for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
- CVSymbol ref_sym = m_index->symrecords().readRecord(gid);
- auto kind = ref_sym.kind();
+ CVSymbol sym = m_index->symrecords().readRecord(gid);
+ auto kind = sym.kind();
+
+ // If this is a global variable, we only need to look at the name
+ llvm::StringRef name;
+ switch (kind) {
+ case SymbolKind::S_GDATA32:
+ case SymbolKind::S_LDATA32: {
+ DataSym data =
+ llvm::cantFail(SymbolDeserializer::deserializeAs<DataSym>(sym));
+ name = data.Name;
+ break;
+ }
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32: {
+ ThreadLocalDataSym data = llvm::cantFail(
+ SymbolDeserializer::deserializeAs<ThreadLocalDataSym>(sym));
+ name = data.Name;
+ break;
+ }
+ case SymbolKind::S_CONSTANT: {
+ ConstantSym data =
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ConstantSym>(sym));
+ name = data.Name;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!name.empty()) {
+ llvm::StringRef base = MSVCUndecoratedNameParser::DropScope(name);
+ if (base.empty())
+ base = name;
+
+ m_global_variable_base_names.Append(ConstString(base), gid);
+ continue;
+ }
+
if (kind != S_PROCREF && kind != S_LPROCREF)
continue;
+ // For functions, we need to follow the reference to the procedure and look
+ // at the type
+
ProcRefSym ref =
- llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym));
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym));
if (ref.Name.empty())
continue;
@@ -1694,7 +1736,7 @@ void SymbolFileNativePDB::CacheFunctionNames() {
// The function/procedure symbol only contains the demangled name.
// The mangled names are in the publics table. Save the address of this
// function to lookup the mangled name later.
- addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid);
+ func_addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid);
llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name);
if (basename.empty())
@@ -1729,43 +1771,47 @@ void SymbolFileNativePDB::CacheFunctionNames() {
continue;
// Check if this symbol is for one of our functions.
- auto it = addr_ids.find({pub.Segment, pub.Offset});
- if (it != addr_ids.end())
+ auto it = func_addr_ids.find({pub.Segment, pub.Offset});
+ if (it != func_addr_ids.end())
m_func_full_names.Append(ConstString(pub.Name), it->second);
}
// Sort them before value searching is working properly.
- m_func_full_names.Sort();
+ m_func_full_names.Sort(std::less<uint32_t>());
m_func_full_names.SizeToFit();
- m_func_method_names.Sort();
+ m_func_method_names.Sort(std::less<uint32_t>());
m_func_method_names.SizeToFit();
- m_func_base_names.Sort();
+ m_func_base_names.Sort(std::less<uint32_t>());
m_func_base_names.SizeToFit();
+ m_global_variable_base_names.Sort(std::less<uint32_t>());
+ m_global_variable_base_names.SizeToFit();
}
void SymbolFileNativePDB::FindGlobalVariables(
ConstString name, const CompilerDeclContext &parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
- using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
- std::vector<SymbolAndOffset> results = m_index->globals().findRecordsByName(
- name.GetStringRef(), m_index->symrecords());
- for (const SymbolAndOffset &result : results) {
- switch (result.second.kind()) {
- case SymbolKind::S_GDATA32:
- case SymbolKind::S_LDATA32:
- case SymbolKind::S_GTHREAD32:
- case SymbolKind::S_LTHREAD32:
- case SymbolKind::S_CONSTANT: {
- PdbGlobalSymId global(result.first, false);
- if (VariableSP var = GetOrCreateGlobalVariable(global))
- variables.AddVariable(var);
- break;
- }
- default:
+ CacheGlobalBaseNames();
+
+ std::vector<uint32_t> results;
+ m_global_variable_base_names.GetValues(name, results);
+
+ size_t n_matches = 0;
+ for (uint32_t gid : results) {
+ PdbGlobalSymId global(gid, false);
+
+ if (parent_decl_ctx.IsValid() &&
+ GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx)
continue;
- }
+
+ VariableSP var = GetOrCreateGlobalVariable(global);
+ if (!var)
+ continue;
+ variables.AddVariable(var);
+
+ if (++n_matches >= max_matches)
+ break;
}
}
@@ -1783,7 +1829,7 @@ void SymbolFileNativePDB::FindFunctions(
name_type_mask & eFunctionNameTypeBase ||
name_type_mask & eFunctionNameTypeMethod))
return;
- CacheFunctionNames();
+ CacheGlobalBaseNames();
std::set<uint32_t> resolved_ids; // avoid duplicate lookups
auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) {
@@ -2426,7 +2472,7 @@ void SymbolFileNativePDB::BuildParentMap() {
// After calling Append(), the type-name map needs to be sorted again to be
// able to look up a type by its name.
- m_type_base_names.Sort();
+ m_type_base_names.Sort(std::less<uint32_t>());
// Now that we know the forward -> full mapping of all type indices, we can
// re-write all the indices. At the end of this process, we want a mapping
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index cfa00416d967..095b40c72c52 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -260,7 +260,10 @@ private:
std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
- void CacheFunctionNames();
+ /// Caches the basenames of symbols found in the globals stream.
+ ///
+ /// This includes functions and global variables
+ void CacheGlobalBaseNames();
void CacheUdtDeclarations();
llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id);
@@ -306,6 +309,9 @@ private:
lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
/// method basename -> Global ID(s)
lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
+
+ /// global variable basename -> Global ID(s)
+ lldb_private::UniqueCStringMap<uint32_t> m_global_variable_base_names;
};
} // namespace npdb
diff --git a/lldb/source/Protocol/MCP/CMakeLists.txt b/lldb/source/Protocol/MCP/CMakeLists.txt
index a73e7e6a7cab..5258cb61a7d1 100644
--- a/lldb/source/Protocol/MCP/CMakeLists.txt
+++ b/lldb/source/Protocol/MCP/CMakeLists.txt
@@ -3,10 +3,12 @@ add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES
Protocol.cpp
Server.cpp
Tool.cpp
+ Transport.cpp
LINK_COMPONENTS
Support
LINK_LIBS
+ lldbHost
lldbUtility
)
diff --git a/lldb/source/Protocol/MCP/Protocol.cpp b/lldb/source/Protocol/MCP/Protocol.cpp
index 65ddfaee7016..0988f456adc2 100644
--- a/lldb/source/Protocol/MCP/Protocol.cpp
+++ b/lldb/source/Protocol/MCP/Protocol.cpp
@@ -167,32 +167,6 @@ bool operator==(const Notification &a, const Notification &b) {
return a.method == b.method && a.params == b.params;
}
-llvm::json::Value toJSON(const ToolCapability &TC) {
- return llvm::json::Object{{"listChanged", TC.listChanged}};
-}
-
-bool fromJSON(const llvm::json::Value &V, ToolCapability &TC,
- llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("listChanged", TC.listChanged);
-}
-
-llvm::json::Value toJSON(const ResourceCapability &RC) {
- return llvm::json::Object{{"listChanged", RC.listChanged},
- {"subscribe", RC.subscribe}};
-}
-
-bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC,
- llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("listChanged", RC.listChanged) &&
- O.map("subscribe", RC.subscribe);
-}
-
-llvm::json::Value toJSON(const Capabilities &C) {
- return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}};
-}
-
bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {
llvm::json::ObjectMapper O(V, P);
return O && O.map("uri", R.uri) && O.map("name", R.name) &&
@@ -209,30 +183,25 @@ llvm::json::Value toJSON(const Resource &R) {
return Result;
}
-bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("tools", C.tools);
-}
-
-llvm::json::Value toJSON(const ResourceContents &RC) {
+llvm::json::Value toJSON(const TextResourceContents &RC) {
llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}};
if (!RC.mimeType.empty())
Result.insert({"mimeType", RC.mimeType});
return Result;
}
-bool fromJSON(const llvm::json::Value &V, ResourceContents &RC,
+bool fromJSON(const llvm::json::Value &V, TextResourceContents &RC,
llvm::json::Path P) {
llvm::json::ObjectMapper O(V, P);
return O && O.map("uri", RC.uri) && O.map("text", RC.text) &&
O.mapOptional("mimeType", RC.mimeType);
}
-llvm::json::Value toJSON(const ResourceResult &RR) {
+llvm::json::Value toJSON(const ReadResourceResult &RR) {
return llvm::json::Object{{"contents", RR.contents}};
}
-bool fromJSON(const llvm::json::Value &V, ResourceResult &RR,
+bool fromJSON(const llvm::json::Value &V, ReadResourceResult &RR,
llvm::json::Path P) {
llvm::json::ObjectMapper O(V, P);
return O && O.map("contents", RR.contents);
@@ -247,15 +216,6 @@ bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) {
return O && O.map("text", TC.text);
}
-llvm::json::Value toJSON(const TextResult &TR) {
- return llvm::json::Object{{"content", TR.content}, {"isError", TR.isError}};
-}
-
-bool fromJSON(const llvm::json::Value &V, TextResult &TR, llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("content", TR.content) && O.map("isError", TR.isError);
-}
-
llvm::json::Value toJSON(const ToolDefinition &TD) {
llvm::json::Object Result{{"name", TD.name}};
if (!TD.description.empty())
@@ -325,4 +285,159 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
return false;
}
+json::Value toJSON(const Implementation &I) {
+ json::Object result{{"name", I.name}, {"version", I.version}};
+
+ if (!I.title.empty())
+ result.insert({"title", I.title});
+
+ return result;
+}
+
+bool fromJSON(const json::Value &V, Implementation &I, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("name", I.name) && O.mapOptional("title", I.title) &&
+ O.mapOptional("version", I.version);
+}
+
+json::Value toJSON(const ClientCapabilities &C) { return json::Object{}; }
+
+bool fromJSON(const json::Value &, ClientCapabilities &, json::Path) {
+ return true;
+}
+
+json::Value toJSON(const ServerCapabilities &C) {
+ json::Object result{};
+
+ if (C.supportsToolsList)
+ result.insert({"tools", json::Object{{"listChanged", true}}});
+
+ if (C.supportsResourcesList || C.supportsResourcesSubscribe) {
+ json::Object resources;
+ if (C.supportsResourcesList)
+ resources.insert({"listChanged", true});
+ if (C.supportsResourcesSubscribe)
+ resources.insert({"subscribe", true});
+ result.insert({"resources", std::move(resources)});
+ }
+
+ if (C.supportsCompletions)
+ result.insert({"completions", json::Object{}});
+
+ if (C.supportsLogging)
+ result.insert({"logging", json::Object{}});
+
+ return result;
+}
+
+bool fromJSON(const json::Value &V, ServerCapabilities &C, json::Path P) {
+ const json::Object *O = V.getAsObject();
+ if (!O) {
+ P.report("expected object");
+ return false;
+ }
+
+ if (O->find("tools") != O->end())
+ C.supportsToolsList = true;
+
+ return true;
+}
+
+json::Value toJSON(const InitializeParams &P) {
+ return json::Object{
+ {"protocolVersion", P.protocolVersion},
+ {"capabilities", P.capabilities},
+ {"clientInfo", P.clientInfo},
+ };
+}
+
+bool fromJSON(const json::Value &V, InitializeParams &I, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("protocolVersion", I.protocolVersion) &&
+ O.map("capabilities", I.capabilities) &&
+ O.map("clientInfo", I.clientInfo);
+}
+
+json::Value toJSON(const InitializeResult &R) {
+ json::Object result{{"protocolVersion", R.protocolVersion},
+ {"capabilities", R.capabilities},
+ {"serverInfo", R.serverInfo}};
+
+ if (!R.instructions.empty())
+ result.insert({"instructions", R.instructions});
+
+ return result;
+}
+
+bool fromJSON(const json::Value &V, InitializeResult &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("protocolVersion", R.protocolVersion) &&
+ O.map("capabilities", R.capabilities) &&
+ O.map("serverInfo", R.serverInfo) &&
+ O.mapOptional("instructions", R.instructions);
+}
+
+json::Value toJSON(const ListToolsResult &R) {
+ return json::Object{{"tools", R.tools}};
+}
+
+bool fromJSON(const json::Value &V, ListToolsResult &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("tools", R.tools);
+}
+
+json::Value toJSON(const CallToolResult &R) {
+ json::Object result{{"content", R.content}};
+
+ if (R.isError)
+ result.insert({"isError", R.isError});
+ if (R.structuredContent)
+ result.insert({"structuredContent", *R.structuredContent});
+
+ return result;
+}
+
+bool fromJSON(const json::Value &V, CallToolResult &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("content", R.content) &&
+ O.mapOptional("isError", R.isError) &&
+ mapRaw(V, "structuredContent", R.structuredContent, P);
+}
+
+json::Value toJSON(const CallToolParams &R) {
+ json::Object result{{"name", R.name}};
+
+ if (R.arguments)
+ result.insert({"arguments", *R.arguments});
+
+ return result;
+}
+
+bool fromJSON(const json::Value &V, CallToolParams &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("name", R.name) && mapRaw(V, "arguments", R.arguments, P);
+}
+
+json::Value toJSON(const ReadResourceParams &R) {
+ return json::Object{{"uri", R.uri}};
+}
+
+bool fromJSON(const json::Value &V, ReadResourceParams &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("uri", R.uri);
+}
+
+json::Value toJSON(const ListResourcesResult &R) {
+ return json::Object{{"resources", R.resources}};
+}
+
+bool fromJSON(const json::Value &V, ListResourcesResult &R, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("resources", R.resources);
+}
+
+json::Value toJSON(const Void &R) { return json::Object{}; }
+
+bool fromJSON(const json::Value &V, Void &R, json::Path P) { return true; }
+
} // namespace lldb_protocol::mcp
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index a9c1482e3e37..f3489c620832 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -7,13 +7,115 @@
//===----------------------------------------------------------------------===//
#include "lldb/Protocol/MCP/Server.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/JSONTransport.h"
#include "lldb/Protocol/MCP/MCPError.h"
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Signals.h"
-using namespace lldb_protocol::mcp;
using namespace llvm;
+using namespace lldb_private;
+using namespace lldb_protocol::mcp;
+
+ServerInfoHandle::ServerInfoHandle() : ServerInfoHandle("") {}
+
+ServerInfoHandle::ServerInfoHandle(StringRef filename) : m_filename(filename) {
+ if (!m_filename.empty())
+ sys::RemoveFileOnSignal(m_filename);
+}
+
+ServerInfoHandle::~ServerInfoHandle() {
+ if (m_filename.empty())
+ return;
+
+ sys::fs::remove(m_filename);
+ sys::DontRemoveFileOnSignal(m_filename);
+ m_filename.clear();
+}
+
+ServerInfoHandle::ServerInfoHandle(ServerInfoHandle &&other)
+ : m_filename(other.m_filename) {
+ *this = std::move(other);
+}
+
+ServerInfoHandle &
+ServerInfoHandle::operator=(ServerInfoHandle &&other) noexcept {
+ m_filename = other.m_filename;
+ other.m_filename.clear();
+ return *this;
+}
+
+json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
+ return json::Object{{"connection_uri", SM.connection_uri}};
+}
+
+bool lldb_protocol::mcp::fromJSON(const json::Value &V, ServerInfo &SM,
+ json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("connection_uri", SM.connection_uri);
+}
-Server::Server(std::string name, std::string version)
- : m_name(std::move(name)), m_version(std::move(version)) {
+Expected<ServerInfoHandle> ServerInfo::Write(const ServerInfo &info) {
+ std::string buf = formatv("{0}", toJSON(info)).str();
+ size_t num_bytes = buf.size();
+
+ FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+
+ Status error(sys::fs::create_directory(user_lldb_dir.GetPath()));
+ if (error.Fail())
+ return error.takeError();
+
+ FileSpec mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
+ formatv("lldb-mcp-{0}.json", getpid()).str());
+
+ const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate;
+ Expected<lldb::FileUP> file =
+ FileSystem::Instance().Open(mcp_registry_entry_path, flags);
+ if (!file)
+ return file.takeError();
+ if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
+ return error;
+ return ServerInfoHandle{mcp_registry_entry_path.GetPath()};
+}
+
+Expected<std::vector<ServerInfo>> ServerInfo::Load() {
+ namespace path = llvm::sys::path;
+ FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+ FileSystem &fs = FileSystem::Instance();
+ std::error_code EC;
+ vfs::directory_iterator it = fs.DirBegin(user_lldb_dir, EC);
+ vfs::directory_iterator end;
+ std::vector<ServerInfo> infos;
+ for (; it != end && !EC; it.increment(EC)) {
+ auto &entry = *it;
+ auto path = entry.path();
+ auto name = path::filename(path);
+ if (!name.starts_with("lldb-mcp-") || !name.ends_with(".json"))
+ continue;
+
+ auto buffer = fs.CreateDataBuffer(path);
+ auto info = json::parse<ServerInfo>(toStringRef(buffer->GetData()));
+ if (!info)
+ return info.takeError();
+
+ infos.emplace_back(std::move(*info));
+ }
+
+ return infos;
+}
+
+Server::Server(std::string name, std::string version,
+ std::unique_ptr<MCPTransport> transport_up,
+ lldb_private::MainLoop &loop)
+ : m_name(std::move(name)), m_version(std::move(version)),
+ m_transport_up(std::move(transport_up)), m_loop(loop) {
AddRequestHandlers();
}
@@ -30,7 +132,7 @@ void Server::AddRequestHandlers() {
this, std::placeholders::_1));
}
-llvm::Expected<Response> Server::Handle(Request request) {
+llvm::Expected<Response> Server::Handle(const Request &request) {
auto it = m_request_handlers.find(request.method);
if (it != m_request_handlers.end()) {
llvm::Expected<Response> response = it->second(request);
@@ -44,7 +146,7 @@ llvm::Expected<Response> Server::Handle(Request request) {
llvm::formatv("no handler for request: {0}", request.method).str());
}
-void Server::Handle(Notification notification) {
+void Server::Handle(const Notification &notification) {
auto it = m_notification_handlers.find(notification.method);
if (it != m_notification_handlers.end()) {
it->second(notification);
@@ -52,49 +154,7 @@ void Server::Handle(Notification notification) {
}
}
-llvm::Expected<std::optional<Message>>
-Server::HandleData(llvm::StringRef data) {
- auto message = llvm::json::parse<Message>(/*JSON=*/data);
- if (!message)
- return message.takeError();
-
- if (const Request *request = std::get_if<Request>(&(*message))) {
- llvm::Expected<Response> response = Handle(*request);
-
- // Handle failures by converting them into an Error message.
- if (!response) {
- Error protocol_error;
- llvm::handleAllErrors(
- response.takeError(),
- [&](const MCPError &err) { protocol_error = err.toProtocolError(); },
- [&](const llvm::ErrorInfoBase &err) {
- protocol_error.code = MCPError::kInternalError;
- protocol_error.message = err.message();
- });
- Response error_response;
- error_response.id = request->id;
- error_response.result = std::move(protocol_error);
- return error_response;
- }
-
- return *response;
- }
-
- if (const Notification *notification =
- std::get_if<Notification>(&(*message))) {
- Handle(*notification);
- return std::nullopt;
- }
-
- if (std::get_if<Response>(&(*message)))
- return llvm::createStringError("unexpected MCP message: response");
-
- llvm_unreachable("all message types handled");
-}
-
void Server::AddTool(std::unique_ptr<Tool> tool) {
- std::lock_guard<std::mutex> guard(m_mutex);
-
if (!tool)
return;
m_tools[tool->GetName()] = std::move(tool);
@@ -102,42 +162,39 @@ void Server::AddTool(std::unique_ptr<Tool> tool) {
void Server::AddResourceProvider(
std::unique_ptr<ResourceProvider> resource_provider) {
- std::lock_guard<std::mutex> guard(m_mutex);
-
if (!resource_provider)
return;
m_resource_providers.push_back(std::move(resource_provider));
}
void Server::AddRequestHandler(llvm::StringRef method, RequestHandler handler) {
- std::lock_guard<std::mutex> guard(m_mutex);
m_request_handlers[method] = std::move(handler);
}
void Server::AddNotificationHandler(llvm::StringRef method,
NotificationHandler handler) {
- std::lock_guard<std::mutex> guard(m_mutex);
m_notification_handlers[method] = std::move(handler);
}
llvm::Expected<Response> Server::InitializeHandler(const Request &request) {
Response response;
- response.result = llvm::json::Object{
- {"protocolVersion", mcp::kProtocolVersion},
- {"capabilities", GetCapabilities()},
- {"serverInfo",
- llvm::json::Object{{"name", m_name}, {"version", m_version}}}};
+ InitializeResult result;
+ result.protocolVersion = mcp::kProtocolVersion;
+ result.capabilities = GetCapabilities();
+ result.serverInfo.name = m_name;
+ result.serverInfo.version = m_version;
+ response.result = std::move(result);
return response;
}
llvm::Expected<Response> Server::ToolsListHandler(const Request &request) {
Response response;
- llvm::json::Array tools;
+ ListToolsResult result;
for (const auto &tool : m_tools)
- tools.emplace_back(toJSON(tool.second->GetDefinition()));
+ result.tools.emplace_back(tool.second->GetDefinition());
- response.result = llvm::json::Object{{"tools", std::move(tools)}};
+ response.result = std::move(result);
return response;
}
@@ -147,16 +204,12 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) {
if (!request.params)
return llvm::createStringError("no tool parameters");
+ CallToolParams params;
+ json::Path::Root root("params");
+ if (!fromJSON(request.params, params, root))
+ return root.getError();
- const json::Object *param_obj = request.params->getAsObject();
- if (!param_obj)
- return llvm::createStringError("no tool parameters");
-
- const json::Value *name = param_obj->get("name");
- if (!name)
- return llvm::createStringError("no tool name");
-
- llvm::StringRef tool_name = name->getAsString().value_or("");
+ llvm::StringRef tool_name = params.name;
if (tool_name.empty())
return llvm::createStringError("no tool name");
@@ -165,10 +218,10 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) {
return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name));
ToolArguments tool_args;
- if (const json::Value *args = param_obj->get("arguments"))
- tool_args = *args;
+ if (params.arguments)
+ tool_args = *params.arguments;
- llvm::Expected<TextResult> text_result = it->second->Call(tool_args);
+ llvm::Expected<CallToolResult> text_result = it->second->Call(tool_args);
if (!text_result)
return text_result.takeError();
@@ -180,15 +233,13 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) {
llvm::Expected<Response> Server::ResourcesListHandler(const Request &request) {
Response response;
- llvm::json::Array resources;
-
- std::lock_guard<std::mutex> guard(m_mutex);
+ ListResourcesResult result;
for (std::unique_ptr<ResourceProvider> &resource_provider_up :
- m_resource_providers) {
+ m_resource_providers)
for (const Resource &resource : resource_provider_up->GetResources())
- resources.push_back(resource);
- }
- response.result = llvm::json::Object{{"resources", std::move(resources)}};
+ result.resources.push_back(resource);
+
+ response.result = std::move(result);
return response;
}
@@ -199,22 +250,18 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) {
if (!request.params)
return llvm::createStringError("no resource parameters");
- const json::Object *param_obj = request.params->getAsObject();
- if (!param_obj)
- return llvm::createStringError("no resource parameters");
-
- const json::Value *uri = param_obj->get("uri");
- if (!uri)
- return llvm::createStringError("no resource uri");
+ ReadResourceParams params;
+ json::Path::Root root("params");
+ if (!fromJSON(request.params, params, root))
+ return root.getError();
- llvm::StringRef uri_str = uri->getAsString().value_or("");
+ llvm::StringRef uri_str = params.uri;
if (uri_str.empty())
return llvm::createStringError("no resource uri");
- std::lock_guard<std::mutex> guard(m_mutex);
for (std::unique_ptr<ResourceProvider> &resource_provider_up :
m_resource_providers) {
- llvm::Expected<ResourceResult> result =
+ llvm::Expected<ReadResourceResult> result =
resource_provider_up->ReadResource(uri_str);
if (result.errorIsA<UnsupportedURI>()) {
llvm::consumeError(result.takeError());
@@ -232,3 +279,71 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) {
llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
MCPError::kResourceNotFound);
}
+
+ServerCapabilities Server::GetCapabilities() {
+ lldb_protocol::mcp::ServerCapabilities capabilities;
+ capabilities.supportsToolsList = true;
+ // FIXME: Support sending notifications when a debugger/target are
+ // added/removed.
+ capabilities.supportsResourcesList = false;
+ return capabilities;
+}
+
+llvm::Error Server::Run() {
+ auto handle = m_transport_up->RegisterMessageHandler(m_loop, *this);
+ if (!handle)
+ return handle.takeError();
+
+ lldb_private::Status status = m_loop.Run();
+ if (status.Fail())
+ return status.takeError();
+
+ return llvm::Error::success();
+}
+
+void Server::Received(const Request &request) {
+ auto SendResponse = [this](const Response &response) {
+ if (llvm::Error error = m_transport_up->Send(response))
+ m_transport_up->Log(llvm::toString(std::move(error)));
+ };
+
+ llvm::Expected<Response> response = Handle(request);
+ if (response)
+ return SendResponse(*response);
+
+ lldb_protocol::mcp::Error protocol_error;
+ llvm::handleAllErrors(
+ response.takeError(),
+ [&](const MCPError &err) { protocol_error = err.toProtocolError(); },
+ [&](const llvm::ErrorInfoBase &err) {
+ protocol_error.code = MCPError::kInternalError;
+ protocol_error.message = err.message();
+ });
+ Response error_response;
+ error_response.id = request.id;
+ error_response.result = std::move(protocol_error);
+ SendResponse(error_response);
+}
+
+void Server::Received(const Response &response) {
+ m_transport_up->Log("unexpected MCP message: response");
+}
+
+void Server::Received(const Notification &notification) {
+ Handle(notification);
+}
+
+void Server::OnError(llvm::Error error) {
+ m_transport_up->Log(llvm::toString(std::move(error)));
+ TerminateLoop();
+}
+
+void Server::OnClosed() {
+ m_transport_up->Log("EOF");
+ TerminateLoop();
+}
+
+void Server::TerminateLoop() {
+ m_loop.AddPendingCallback(
+ [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+}
diff --git a/lldb/source/Protocol/MCP/Transport.cpp b/lldb/source/Protocol/MCP/Transport.cpp
new file mode 100644
index 000000000000..cccdc3b5bd65
--- /dev/null
+++ b/lldb/source/Protocol/MCP/Transport.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Protocol/MCP/Transport.h"
+#include "llvm/ADT/StringRef.h"
+#include <utility>
+
+using namespace lldb_protocol::mcp;
+using namespace llvm;
+
+Transport::Transport(lldb::IOObjectSP in, lldb::IOObjectSP out,
+ LogCallback log_callback)
+ : JSONRPCTransport(in, out), m_log_callback(std::move(log_callback)) {}
+
+void Transport::Log(StringRef message) {
+ if (m_log_callback)
+ m_log_callback(message);
+}
diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp
index 7efce2a03550..9a79b3c62762 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -663,7 +663,7 @@ ObjectFile::GetDWARFSectionTypeFromName(llvm::StringRef name) {
.Case("rnglists.dwo", eSectionTypeDWARFDebugRngListsDwo)
.Case("str", eSectionTypeDWARFDebugStr)
.Case("str.dwo", eSectionTypeDWARFDebugStrDwo)
- .Case("str_offsets", eSectionTypeDWARFDebugStrOffsets)
+ .Cases("str_offsets", "str_offs", eSectionTypeDWARFDebugStrOffsets)
.Case("str_offsets.dwo", eSectionTypeDWARFDebugStrOffsetsDwo)
.Case("tu_index", eSectionTypeDWARFDebugTuIndex)
.Case("types", eSectionTypeDWARFDebugTypes)
diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp
index b86fef6bf03e..3c5107434014 100644
--- a/lldb/source/Target/ABI.cpp
+++ b/lldb/source/Target/ABI.cpp
@@ -232,13 +232,13 @@ bool ABI::GetFallbackRegisterLocation(
}
std::unique_ptr<llvm::MCRegisterInfo> ABI::MakeMCRegisterInfo(const ArchSpec &arch) {
- std::string triple = arch.GetTriple().getTriple();
+ const llvm::Triple &triple = arch.GetTriple();
std::string lookup_error;
const llvm::Target *target =
llvm::TargetRegistry::lookupTarget(triple, lookup_error);
if (!target) {
LLDB_LOG(GetLog(LLDBLog::Process),
- "Failed to create an llvm target for {0}: {1}", triple,
+ "Failed to create an llvm target for {0}: {1}", triple.str(),
lookup_error);
return nullptr;
}
diff --git a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
index 7f82581cc601..aef895def793 100644
--- a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
+++ b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
@@ -8,13 +8,20 @@
#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
+#include "lldb/Core/Module.h"
#include "lldb/Target/InstrumentationRuntime.h"
#include "lldb/Target/Process.h"
+#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-private.h"
using namespace lldb;
using namespace lldb_private;
+static bool IsStoppedInDarwinSanitizer(Thread &thread, Module &module) {
+ return module.GetFileSpec().GetFilename().GetStringRef().starts_with(
+ "libclang_rt.");
+}
+
InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(
Thread &thread, std::string description,
StructuredData::ObjectSP additional_data)
@@ -34,3 +41,38 @@ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
return StopInfoSP(
new InstrumentationRuntimeStopInfo(thread, description, additionalData));
}
+
+std::optional<uint32_t>
+InstrumentationRuntimeStopInfo::GetSuggestedStackFrameIndex(
+ bool inlined_stack) {
+ ThreadSP thread_sp = GetThread();
+ if (!thread_sp)
+ return std::nullopt;
+
+ // Defensive upper-bound of when we stop walking up the frames in
+ // case we somehow ended up looking at an infinite recursion.
+ constexpr size_t max_stack_depth = 128;
+
+ // Start at parent frame.
+ size_t stack_idx = 1;
+ StackFrameSP most_relevant_frame_sp =
+ thread_sp->GetStackFrameAtIndex(stack_idx);
+
+ while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
+ auto const &sc =
+ most_relevant_frame_sp->GetSymbolContext(lldb::eSymbolContextModule);
+
+ if (!sc.module_sp)
+ return std::nullopt;
+
+ // Found a frame outside of the sanitizer runtime libraries.
+ // That's the one we want to display.
+ if (!IsStoppedInDarwinSanitizer(*thread_sp, *sc.module_sp))
+ return stack_idx;
+
+ ++stack_idx;
+ most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(stack_idx);
+ }
+
+ return stack_idx;
+}
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index ff9e5fc12059..3176852f0b72 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -4269,6 +4269,14 @@ bool Process::ProcessEventData::ShouldStop(Event *event_ptr,
// appropriately. We also need to stop processing actions, since they
// aren't expecting the target to be running.
+ // Clear the selected frame which may have been set as part of utility
+ // expressions that have been run as part of this stop. If we didn't
+ // clear this, then StopInfo::GetSuggestedStackFrameIndex would not
+ // take affect when we next called SelectMostRelevantFrame.
+ // PerformAction should not be the one setting a selected frame, instead
+ // this should be done via GetSuggestedStackFrameIndex.
+ thread_sp->ClearSelectedFrameIndex();
+
// FIXME: we might have run.
if (stop_info_sp->HasTargetRunSinceMe()) {
SetRestarted(true);
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index bcf1297f2114..787eb94be3b4 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -293,6 +293,9 @@ void RegisterContextUnwind::InitializeZerothFrame() {
return;
}
+ // Give the Architecture a chance to replace the UnwindPlan.
+ TryAdoptArchitectureUnwindPlan();
+
UnwindLogMsg("initialized frame current pc is 0x%" PRIx64 " cfa is 0x%" PRIx64
" afa is 0x%" PRIx64 " using %s UnwindPlan",
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
@@ -482,6 +485,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() {
}
}
+ // Give the Architecture a chance to replace the UnwindPlan.
+ TryAdoptArchitectureUnwindPlan();
+
UnwindLogMsg("initialized frame cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
(uint64_t)m_cfa, (uint64_t)m_afa);
return;
@@ -686,6 +692,9 @@ void RegisterContextUnwind::InitializeNonZerothFrame() {
}
}
+ // Give the Architecture a chance to replace the UnwindPlan.
+ TryAdoptArchitectureUnwindPlan();
+
UnwindLogMsg("initialized frame current pc is 0x%" PRIx64
" cfa is 0x%" PRIx64 " afa is 0x%" PRIx64,
(uint64_t)m_current_pc.GetLoadAddress(exe_ctx.GetTargetPtr()),
@@ -1717,6 +1726,41 @@ RegisterContextUnwind::SavedLocationForRegister(
return UnwindLLDB::RegisterSearchResult::eRegisterNotFound;
}
+UnwindPlanSP RegisterContextUnwind::TryAdoptArchitectureUnwindPlan() {
+ if (!m_full_unwind_plan_sp)
+ return {};
+ ProcessSP process_sp = m_thread.GetProcess();
+ if (!process_sp)
+ return {};
+
+ UnwindPlanSP arch_override_plan_sp;
+ if (Architecture *arch = process_sp->GetTarget().GetArchitecturePlugin())
+ arch_override_plan_sp =
+ arch->GetArchitectureUnwindPlan(m_thread, this, m_full_unwind_plan_sp);
+
+ if (arch_override_plan_sp) {
+ m_full_unwind_plan_sp = arch_override_plan_sp;
+ PropagateTrapHandlerFlagFromUnwindPlan(m_full_unwind_plan_sp);
+ m_registers.clear();
+ if (GetLog(LLDBLog::Unwind)) {
+ UnwindLogMsg(
+ "Replacing Full Unwindplan with Architecture UnwindPlan, '%s'",
+ m_full_unwind_plan_sp->GetSourceName().AsCString());
+ const UnwindPlan::Row *active_row =
+ m_full_unwind_plan_sp->GetRowForFunctionOffset(m_current_offset);
+ if (active_row) {
+ StreamString active_row_strm;
+ active_row->Dump(active_row_strm, m_full_unwind_plan_sp.get(),
+ &m_thread,
+ m_start_pc.GetLoadAddress(&process_sp->GetTarget()));
+ UnwindLogMsg("%s", active_row_strm.GetData());
+ }
+ }
+ }
+
+ return {};
+}
+
// TryFallbackUnwindPlan() -- this method is a little tricky.
//
// When this is called, the frame above -- the caller frame, the "previous"
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 40049d40aa41..2ed58c5331df 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -57,14 +57,14 @@ using namespace lldb_private;
StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
user_id_t unwind_frame_index, addr_t cfa,
bool cfa_is_valid, addr_t pc, StackFrame::Kind kind,
- bool behaves_like_zeroth_frame,
+ bool artificial, bool behaves_like_zeroth_frame,
const SymbolContext *sc_ptr)
: m_thread_wp(thread_sp), m_frame_index(frame_idx),
m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(),
m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()),
m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
- m_stack_frame_kind(kind),
+ m_stack_frame_kind(kind), m_artificial(artificial),
m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
m_variable_list_sp(), m_variable_list_value_objects(),
m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -92,7 +92,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()),
m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(),
m_frame_base_error(), m_cfa_is_valid(true),
- m_stack_frame_kind(StackFrame::Kind::Regular),
+ m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false),
m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
m_variable_list_sp(), m_variable_list_value_objects(),
m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -120,7 +120,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
nullptr, thread_sp->GetProcess().get()),
m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(),
m_frame_base_error(), m_cfa_is_valid(true),
- m_stack_frame_kind(StackFrame::Kind::Regular),
+ m_stack_frame_kind(StackFrame::Kind::Regular), m_artificial(false),
m_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
m_variable_list_sp(), m_variable_list_value_objects(),
m_recognized_frame_sp(), m_disassembly(), m_mutex() {
@@ -266,6 +266,7 @@ bool StackFrame::ChangePC(addr_t pc) {
const char *StackFrame::Disassemble() {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
if (!m_disassembly.Empty())
return m_disassembly.GetData();
@@ -440,10 +441,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals,
const bool get_child_variables = true;
const bool can_create = true;
const bool stop_if_child_block_is_inlined_function = true;
- frame_block->AppendBlockVariables(can_create, get_child_variables,
- stop_if_child_block_is_inlined_function,
- [](Variable *v) { return true; },
- m_variable_list_sp.get());
+ frame_block->AppendBlockVariables(
+ can_create, get_child_variables,
+ stop_if_child_block_is_inlined_function,
+ [](Variable *v) { return true; }, m_variable_list_sp.get());
}
}
@@ -1227,10 +1228,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp,
VariableList *var_list = GetVariableList(true, nullptr);
if (var_list) {
// Make sure the variable is a frame variable
- const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get());
+ const uint32_t var_idx =
+ var_list->FindIndexForVariable(variable_sp.get());
const uint32_t num_variables = var_list->GetSize();
if (var_idx < num_variables) {
- valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx);
+ valobj_sp =
+ m_variable_list_value_objects.GetValueObjectAtIndex(var_idx);
if (!valobj_sp) {
if (m_variable_list_value_objects.GetSize() < num_variables)
m_variable_list_value_objects.Resize(num_variables);
@@ -1261,10 +1264,12 @@ bool StackFrame::IsHistorical() const {
return m_stack_frame_kind == StackFrame::Kind::History;
}
-bool StackFrame::IsArtificial() const {
- return m_stack_frame_kind == StackFrame::Kind::Artificial;
+bool StackFrame::IsSynthetic() const {
+ return m_stack_frame_kind == StackFrame::Kind::Synthetic;
}
+bool StackFrame::IsArtificial() const { return m_artificial; }
+
bool StackFrame::IsHidden() {
if (auto recognized_frame_sp = GetRecognizedFrame())
return recognized_frame_sp->ShouldHide();
@@ -1764,11 +1769,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
if (clobbered_reg_matcher(operands[0])) {
origin_operand = &operands[1];
- }
- else if (clobbered_reg_matcher(operands[1])) {
+ } else if (clobbered_reg_matcher(operands[1])) {
origin_operand = &operands[0];
- }
- else {
+ } else {
continue;
}
@@ -1794,8 +1797,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
if (!source_path) {
continue;
}
- source_path =
- GetValueForDereferincingOffset(frame, source_path, offset);
+ source_path = GetValueForDereferincingOffset(frame, source_path, offset);
}
if (source_path) {
@@ -1805,7 +1807,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
return ValueObjectSP();
}
-}
+} // namespace
lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg,
int64_t offset) {
diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp
index aedfc52cfb4c..fa5d159c0c91 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -321,13 +321,14 @@ void StackFrameList::SynthesizeTailCallFrames(StackFrame &next_frame) {
addr_t pc = calleeInfo.address;
// If the callee address refers to the call instruction, we do not want to
// subtract 1 from this value.
+ const bool artificial = true;
const bool behaves_like_zeroth_frame =
calleeInfo.address_type == CallEdge::AddrType::Call;
SymbolContext sc;
callee->CalculateSymbolContext(&sc);
auto synth_frame = std::make_shared<StackFrame>(
m_thread.shared_from_this(), frame_idx, concrete_frame_idx, cfa,
- cfa_is_valid, pc, StackFrame::Kind::Artificial,
+ cfa_is_valid, pc, StackFrame::Kind::Regular, artificial,
behaves_like_zeroth_frame, &sc);
m_frames.push_back(synth_frame);
LLDB_LOG(log, "Pushed frame {0} at {1:x}", callee->GetDisplayName(), pc);
@@ -470,7 +471,8 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx,
const bool cfa_is_valid = true;
unwind_frame_sp = std::make_shared<StackFrame>(
m_thread.shared_from_this(), m_frames.size(), idx, cfa, cfa_is_valid,
- pc, StackFrame::Kind::Regular, behaves_like_zeroth_frame, nullptr);
+ pc, StackFrame::Kind::Regular, false, behaves_like_zeroth_frame,
+ nullptr);
// Create synthetic tail call frames between the previous frame and the
// newly-found frame. The new frame's index may change after this call,
@@ -942,3 +944,5 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t first_frame,
strm.IndentLess();
return num_frames_displayed;
}
+
+void StackFrameList::ClearSelectedFrameIndex() { m_selected_frame_idx.reset(); }
diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp
index b1795970802a..f879276527dd 100644
--- a/lldb/source/Target/StackID.cpp
+++ b/lldb/source/Target/StackID.cpp
@@ -63,16 +63,7 @@ bool lldb_private::operator==(const StackID &lhs, const StackID &rhs) {
}
bool lldb_private::operator!=(const StackID &lhs, const StackID &rhs) {
- if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
- return true;
-
- SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
- SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
-
- if (lhs_scope == nullptr && rhs_scope == nullptr)
- return lhs.GetPC() != rhs.GetPC();
-
- return lhs_scope != rhs_scope;
+ return !(lhs == rhs);
}
bool lldb_private::operator<(const StackID &lhs, const StackID &rhs) {
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index 909f335687b2..8ad8d507268e 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -73,8 +73,9 @@ json::Value ModuleStats::ToJSON() const {
debug_info_had_incomplete_types);
module.try_emplace("symbolTableStripped", symtab_stripped);
module.try_emplace("symbolTableSymbolCount", symtab_symbol_count);
- module.try_emplace("dwoFileCount", dwo_file_count);
- module.try_emplace("loadedDwoFileCount", loaded_dwo_file_count);
+ module.try_emplace("dwoFileCount", dwo_stats.dwo_file_count);
+ module.try_emplace("loadedDwoFileCount", dwo_stats.loaded_dwo_file_count);
+ module.try_emplace("dwoErrorCount", dwo_stats.dwo_error_count);
if (!symbol_locator_time.map.empty()) {
json::Object obj;
@@ -324,8 +325,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
uint32_t num_modules_with_incomplete_types = 0;
uint32_t num_stripped_modules = 0;
uint32_t symtab_symbol_count = 0;
- uint32_t total_loaded_dwo_file_count = 0;
- uint32_t total_dwo_file_count = 0;
+ DWOStats total_dwo_stats;
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
Module *module = target != nullptr
? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -357,10 +357,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
for (const auto &symbol_module : symbol_modules.Modules())
module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
}
- std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) =
- sym_file->GetDwoFileCounts();
- total_dwo_file_count += module_stat.dwo_file_count;
- total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
+ DWOStats current_dwo_stats = sym_file->GetDwoStats();
+ module_stat.dwo_stats += current_dwo_stats;
+ total_dwo_stats += current_dwo_stats;
module_stat.debug_info_index_loaded_from_cache =
sym_file->GetDebugInfoIndexWasLoadedFromCache();
if (module_stat.debug_info_index_loaded_from_cache)
@@ -435,8 +434,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
{"totalDebugInfoEnabled", num_debug_info_enabled_modules},
{"totalSymbolTableStripped", num_stripped_modules},
{"totalSymbolTableSymbolCount", symtab_symbol_count},
- {"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
- {"totalDwoFileCount", total_dwo_file_count},
+ {"totalLoadedDwoFileCount", total_dwo_stats.loaded_dwo_file_count},
+ {"totalDwoFileCount", total_dwo_stats.dwo_file_count},
+ {"totalDwoErrorCount", total_dwo_stats.dwo_error_count},
};
if (include_targets) {
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index ddf8c62e969e..f47dae2b2465 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -108,8 +108,7 @@ public:
void StoreBPInfo() {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
- BreakpointSiteSP bp_site_sp(
- thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (bp_site_sp) {
uint32_t num_constituents = bp_site_sp->GetNumberOfConstituents();
if (num_constituents == 1) {
@@ -139,8 +138,7 @@ public:
bool IsValidForOperatingSystemThread(Thread &thread) override {
ProcessSP process_sp(thread.GetProcess());
if (process_sp) {
- BreakpointSiteSP bp_site_sp(
- process_sp->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (bp_site_sp)
return bp_site_sp->ValidForThisThread(thread);
}
@@ -154,8 +152,7 @@ public:
if (thread_sp) {
if (!m_should_stop_is_valid) {
// Only check once if we should stop at a breakpoint
- BreakpointSiteSP bp_site_sp(
- thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (bp_site_sp) {
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
StoppointCallbackContext context(event_ptr, exe_ctx, true);
@@ -186,8 +183,7 @@ public:
if (m_description.empty()) {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
- BreakpointSiteSP bp_site_sp(
- thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (bp_site_sp) {
StreamString strm;
// If we have just hit an internal breakpoint, and it has a kind
@@ -247,6 +243,35 @@ public:
return m_description.c_str();
}
+ uint32_t GetStopReasonDataCount() const override {
+ lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
+ if (bp_site_sp)
+ return bp_site_sp->GetNumberOfConstituents() * 2;
+ return 0; // Breakpoint must have cleared itself...
+ }
+
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
+ if (bp_site_sp) {
+ uint32_t bp_index = idx / 2;
+ BreakpointLocationSP bp_loc_sp(
+ bp_site_sp->GetConstituentAtIndex(bp_index));
+ if (bp_loc_sp) {
+ if (idx & 1) {
+ // FIXME: This might be a Facade breakpoint, so we need to fetch
+ // the one that the thread actually hit, not the native loc ID.
+
+ // Odd idx, return the breakpoint location ID
+ return bp_loc_sp->GetID();
+ } else {
+ // Even idx, return the breakpoint ID
+ return bp_loc_sp->GetBreakpoint().GetID();
+ }
+ }
+ }
+ return LLDB_INVALID_BREAK_ID;
+ }
+
std::optional<uint32_t>
GetSuggestedStackFrameIndex(bool inlined_stack) override {
if (!inlined_stack)
@@ -255,8 +280,7 @@ public:
ThreadSP thread_sp(m_thread_wp.lock());
if (!thread_sp)
return {};
- BreakpointSiteSP bp_site_sp(
- thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (!bp_site_sp)
return {};
@@ -297,8 +321,7 @@ protected:
return;
}
- BreakpointSiteSP bp_site_sp(
- thread_sp->GetProcess()->GetBreakpointSiteList().FindByID(m_value));
+ BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
std::unordered_set<break_id_t> precondition_breakpoints;
// Breakpoints that fail their condition check are not considered to
// have been hit. If the only locations at this site have failed their
@@ -629,6 +652,20 @@ protected:
}
private:
+ BreakpointSiteSP GetBreakpointSiteSP() const {
+ if (m_value == LLDB_INVALID_BREAK_ID)
+ return {};
+
+ ThreadSP thread_sp = GetThread();
+ if (!thread_sp)
+ return {};
+ ProcessSP process_sp = thread_sp->GetProcess();
+ if (!process_sp)
+ return {};
+
+ return process_sp->GetBreakpointSiteList().FindByID(m_value);
+ }
+
bool m_should_stop;
bool m_should_stop_is_valid;
bool m_should_perform_action; // Since we are trying to preserve the "state"
@@ -699,6 +736,13 @@ public:
StopReason GetStopReason() const override { return eStopReasonWatchpoint; }
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ return 0;
+ }
+
const char *GetDescription() override {
if (m_description.empty()) {
StreamString strm;
@@ -1139,6 +1183,13 @@ public:
bool ShouldSelect() const override { return IsShouldStopSignal(); }
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ return 0;
+ }
+
private:
// In siginfo_t terms, if m_value is si_signo, m_code is si_code.
std::optional<int> m_code;
@@ -1171,6 +1222,14 @@ public:
}
return m_description.c_str();
}
+
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ else
+ return 0;
+ }
};
// StopInfoTrace
@@ -1249,6 +1308,13 @@ public:
else
return m_description.c_str();
}
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ else
+ return 0;
+ }
};
// StopInfoProcessorTrace
@@ -1390,6 +1456,14 @@ public:
const char *GetDescription() override { return "fork"; }
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ else
+ return 0;
+ }
+
protected:
void PerformAction(Event *event_ptr) override {
// Only perform the action once
@@ -1424,6 +1498,13 @@ public:
const char *GetDescription() override { return "vfork"; }
+ uint32_t GetStopReasonDataCount() const override { return 1; }
+ uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
+ if (idx == 0)
+ return GetValue();
+ return 0;
+ }
+
protected:
void PerformAction(Event *event_ptr) override {
// Only perform the action once
diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp
index 8f14cf6e3943..1b8dae39735d 100644
--- a/lldb/source/Utility/ArchSpec.cpp
+++ b/lldb/source/Utility/ArchSpec.cpp
@@ -260,9 +260,9 @@ static_assert(sizeof(g_core_definitions) / sizeof(CoreDefinition) ==
struct ArchDefinitionEntry {
ArchSpec::Core core;
uint32_t cpu;
- uint32_t sub;
- uint32_t cpu_mask;
- uint32_t sub_mask;
+ uint32_t sub = LLDB_INVALID_CPUTYPE;
+ uint32_t cpu_mask = UINT32_MAX;
+ uint32_t sub_mask = UINT32_MAX;
};
struct ArchDefinition {
@@ -357,7 +357,8 @@ static const ArchDefinitionEntry g_macho_arch_entries[] = {
{ArchSpec::eCore_riscv32, llvm::MachO::CPU_TYPE_RISCV, CPU_ANY, UINT32_MAX, SUBTYPE_MASK},
// Catch any unknown mach architectures so we can always use the object and symbol mach-o files
{ArchSpec::eCore_uknownMach32, 0, 0, 0xFF000000u, 0x00000000u},
- {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}};
+ {ArchSpec::eCore_uknownMach64, llvm::MachO::CPU_ARCH_ABI64, 0, 0xFF000000u, 0x00000000u}
+};
// clang-format on
static const ArchDefinition g_macho_arch_def = {eArchTypeMachO,
@@ -369,72 +370,41 @@ static const ArchDefinition g_macho_arch_def = {eArchTypeMachO,
// convert cpu type and subtypes to architecture names, and to convert
// architecture names to cpu types and subtypes. The ordering is important and
// allows the precedence to be set when the table is built.
+// clang-format off
static const ArchDefinitionEntry g_elf_arch_entries[] = {
- {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // Sparc
- {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80386
- {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel MCU // FIXME: is this correct?
- {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC
- {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64,
- ArchSpec::eCore_ppc64le_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64le
- {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64,
- ArchSpec::eCore_ppc64_generic, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC64
- {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM
- {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM64
- {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // SystemZ
- {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // SPARC V9
- {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64
- {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32
- {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2
- {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6
- {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32el
- {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r2el
- {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips32r6el
- {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64
- {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2
- {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6
- {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64el
- {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r2el
- {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS,
- ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu}, // mips64r6el
- {ArchSpec::eCore_msp430, llvm::ELF::EM_MSP430, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // MSP430
- {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // HEXAGON
- {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARC
- {ArchSpec::eCore_avr, llvm::ELF::EM_AVR, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu,
- 0xFFFFFFFFu}, // AVR
- {ArchSpec::eCore_riscv32, llvm::ELF::EM_RISCV,
- ArchSpec::eRISCVSubType_riscv32, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv32
- {ArchSpec::eCore_riscv64, llvm::ELF::EM_RISCV,
- ArchSpec::eRISCVSubType_riscv64, 0xFFFFFFFFu, 0xFFFFFFFFu}, // riscv64
- {ArchSpec::eCore_loongarch32, llvm::ELF::EM_LOONGARCH,
- ArchSpec::eLoongArchSubType_loongarch32, 0xFFFFFFFFu,
- 0xFFFFFFFFu}, // loongarch32
- {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH,
- ArchSpec::eLoongArchSubType_loongarch64, 0xFFFFFFFFu,
- 0xFFFFFFFFu}, // loongarch64
+ {ArchSpec::eCore_sparc_generic, llvm::ELF::EM_SPARC }, // Sparc
+ {ArchSpec::eCore_x86_32_i386, llvm::ELF::EM_386 }, // Intel 80386
+ {ArchSpec::eCore_x86_32_i486, llvm::ELF::EM_IAMCU }, // Intel MCU // FIXME: is this correct?
+ {ArchSpec::eCore_ppc_generic, llvm::ELF::EM_PPC }, // PowerPC
+ {ArchSpec::eCore_ppc64le_generic, llvm::ELF::EM_PPC64, ArchSpec::eCore_ppc64le_generic}, // PowerPC64le
+ {ArchSpec::eCore_ppc64_generic, llvm::ELF::EM_PPC64, ArchSpec::eCore_ppc64_generic}, // PowerPC64
+ {ArchSpec::eCore_arm_generic, llvm::ELF::EM_ARM }, // ARM
+ {ArchSpec::eCore_arm_aarch64, llvm::ELF::EM_AARCH64 }, // ARM64
+ {ArchSpec::eCore_s390x_generic, llvm::ELF::EM_S390 }, // SystemZ
+ {ArchSpec::eCore_sparc9_generic, llvm::ELF::EM_SPARCV9 }, // SPARC V9
+ {ArchSpec::eCore_x86_64_x86_64, llvm::ELF::EM_X86_64 }, // AMD64
+ {ArchSpec::eCore_mips32, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32}, // mips32
+ {ArchSpec::eCore_mips32r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2}, // mips32r2
+ {ArchSpec::eCore_mips32r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6}, // mips32r6
+ {ArchSpec::eCore_mips32el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32el}, // mips32el
+ {ArchSpec::eCore_mips32r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r2el}, // mips32r2el
+ {ArchSpec::eCore_mips32r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips32r6el}, // mips32r6el
+ {ArchSpec::eCore_mips64, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64},
+ {ArchSpec::eCore_mips64r2, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2}, // mips64r2
+ {ArchSpec::eCore_mips64r6, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6}, // mips64r6
+ {ArchSpec::eCore_mips64el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64el}, // mips64el
+ {ArchSpec::eCore_mips64r2el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r2el}, // mips64r2el
+ {ArchSpec::eCore_mips64r6el, llvm::ELF::EM_MIPS, ArchSpec::eMIPSSubType_mips64r6el}, // mips64r6el
+ {ArchSpec::eCore_msp430, llvm::ELF::EM_MSP430 }, // MSP430
+ {ArchSpec::eCore_hexagon_generic, llvm::ELF::EM_HEXAGON }, // HEXAGON
+ {ArchSpec::eCore_arc, llvm::ELF::EM_ARC_COMPACT2}, // ARC
+ {ArchSpec::eCore_avr, llvm::ELF::EM_AVR }, // AVR
+ {ArchSpec::eCore_riscv32, llvm::ELF::EM_RISCV, ArchSpec::eRISCVSubType_riscv32}, // riscv32
+ {ArchSpec::eCore_riscv64, llvm::ELF::EM_RISCV, ArchSpec::eRISCVSubType_riscv64}, // riscv64
+ {ArchSpec::eCore_loongarch32, llvm::ELF::EM_LOONGARCH, ArchSpec::eLoongArchSubType_loongarch32}, // loongarch32
+ {ArchSpec::eCore_loongarch64, llvm::ELF::EM_LOONGARCH, ArchSpec::eLoongArchSubType_loongarch64}, // loongarch64
};
+// clang-format on
static const ArchDefinition g_elf_arch_def = {
eArchTypeELF,
@@ -442,25 +412,18 @@ static const ArchDefinition g_elf_arch_def = {
g_elf_arch_entries,
"elf",
};
-
+// clang-format off
static const ArchDefinitionEntry g_coff_arch_entries[] = {
- {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // Intel 80x86
- {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC
- {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // PowerPC (with FPU)
- {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARM
- {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7
- {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // ARMv7
- {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}, // AMD64
- {ArchSpec::eCore_arm_arm64, llvm::COFF::IMAGE_FILE_MACHINE_ARM64,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu} // ARM64
+ {ArchSpec::eCore_x86_32_i386, llvm::COFF::IMAGE_FILE_MACHINE_I386}, // Intel 80x86
+ {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPC}, // PowerPC
+ {ArchSpec::eCore_ppc_generic, llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP}, // PowerPC (with FPU)
+ {ArchSpec::eCore_arm_generic, llvm::COFF::IMAGE_FILE_MACHINE_ARM}, // ARM
+ {ArchSpec::eCore_arm_armv7, llvm::COFF::IMAGE_FILE_MACHINE_ARMNT}, // ARMv7
+ {ArchSpec::eCore_thumb, llvm::COFF::IMAGE_FILE_MACHINE_THUMB}, // ARMv7
+ {ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64}, // AMD64
+ {ArchSpec::eCore_arm_arm64, llvm::COFF::IMAGE_FILE_MACHINE_ARM64} // ARM64
};
+// clang-format on
static const ArchDefinition g_coff_arch_def = {
eArchTypeCOFF,
@@ -469,11 +432,12 @@ static const ArchDefinition g_coff_arch_def = {
"pe-coff",
};
+// clang-format off
static const ArchDefinitionEntry g_xcoff_arch_entries[] = {
- {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM, LLDB_INVALID_CPUTYPE,
- 0xFFFFFFFFu, 0xFFFFFFFFu},
- {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64,
- LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu}};
+ {ArchSpec::eCore_ppc_generic, llvm::XCOFF::TCPU_COM},
+ {ArchSpec::eCore_ppc64_generic, llvm::XCOFF::TCPU_PPC64}
+};
+// clang-format on
static const ArchDefinition g_xcoff_arch_def = {
eArchTypeXCOFF,
@@ -695,13 +659,9 @@ uint32_t ArchSpec::GetMachOCPUSubType() const {
return LLDB_INVALID_CPUTYPE;
}
-uint32_t ArchSpec::GetDataByteSize() const {
- return 1;
-}
+uint32_t ArchSpec::GetDataByteSize() const { return 1; }
-uint32_t ArchSpec::GetCodeByteSize() const {
- return 1;
-}
+uint32_t ArchSpec::GetCodeByteSize() const { return 1; }
llvm::Triple::ArchType ArchSpec::GetMachine() const {
const CoreDefinition *core_def = FindCoreDefinition(m_core);
@@ -1170,8 +1130,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2,
break;
// v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization
- // Cortex-M0 - ARMv6-M - armv6m
- // Cortex-M3 - ARMv7-M - armv7m
+ // Cortex-M0 - ARMv6-M - armv6m
+ // Cortex-M3 - ARMv7-M - armv7m
// Cortex-M4 - ARMv7E-M - armv7em
case ArchSpec::eCore_arm_armv7em:
if (!enforce_exact_match) {
@@ -1188,8 +1148,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2,
break;
// v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization
- // Cortex-M0 - ARMv6-M - armv6m
- // Cortex-M3 - ARMv7-M - armv7m
+ // Cortex-M0 - ARMv6-M - armv6m
+ // Cortex-M3 - ARMv7-M - armv7m
// Cortex-M4 - ARMv7E-M - armv7em
case ArchSpec::eCore_arm_armv7m:
if (!enforce_exact_match) {
@@ -1206,8 +1166,8 @@ static bool cores_match(const ArchSpec::Core core1, const ArchSpec::Core core2,
break;
// v. https://en.wikipedia.org/wiki/ARM_Cortex-M#Silicon_customization
- // Cortex-M0 - ARMv6-M - armv6m
- // Cortex-M3 - ARMv7-M - armv7m
+ // Cortex-M0 - ARMv6-M - armv6m
+ // Cortex-M3 - ARMv7-M - armv7m
// Cortex-M4 - ARMv7E-M - armv7em
case ArchSpec::eCore_arm_armv6m:
if (!enforce_exact_match) {
@@ -1434,7 +1394,6 @@ bool lldb_private::operator<(const ArchSpec &lhs, const ArchSpec &rhs) {
return lhs_core < rhs_core;
}
-
bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) {
return lhs.GetCore() == rhs.GetCore();
}
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index b1cd824c2299..70564663a62c 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -37,4 +37,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const {
return v->Visit(this);
}
+llvm::Expected<lldb::ValueObjectSP>
+IntegerLiteralNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
+
+llvm::Expected<lldb::ValueObjectSP> FloatLiteralNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
+
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 6f28434c646c..c6cf41ee9e9e 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//
#include "lldb/ValueObject/DILEval.h"
+#include "lldb/Core/Module.h"
#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/ValueObject/DILAST.h"
@@ -330,40 +332,135 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
return lhs_or_err;
lldb::ValueObjectSP base = *lhs_or_err;
- // Check to see if 'base' has a synthetic value; if so, try using that.
+ StreamString var_expr_path_strm;
uint64_t child_idx = node->GetIndex();
- if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) {
- llvm::Expected<uint32_t> num_children =
- synthetic->GetNumChildren(child_idx + 1);
- if (!num_children)
- return llvm::make_error<DILDiagnosticError>(
- m_expr, toString(num_children.takeError()), node->GetLocation());
- if (child_idx >= *num_children) {
- std::string message = llvm::formatv(
- "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ lldb::ValueObjectSP child_valobj_sp;
+
+ bool is_incomplete_array = false;
+ CompilerType base_type = base->GetCompilerType().GetNonReferenceType();
+ base->GetExpressionPath(var_expr_path_strm);
+
+ if (base_type.IsPointerType()) {
+ bool is_objc_pointer = true;
+
+ if (base->GetCompilerType().GetMinimumLanguage() != lldb::eLanguageTypeObjC)
+ is_objc_pointer = false;
+ else if (!base->GetCompilerType().IsPointerType())
+ is_objc_pointer = false;
+
+ if (!m_use_synthetic && is_objc_pointer) {
+ std::string err_msg = llvm::formatv(
+ "\"({0}) {1}\" is an Objective-C pointer, and cannot be subscripted",
base->GetTypeName().AsCString("<invalid type>"),
- base->GetName().AsCString());
- return llvm::make_error<DILDiagnosticError>(m_expr, message,
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
node->GetLocation());
}
- if (lldb::ValueObjectSP child_valobj_sp =
- synthetic->GetChildAtIndex(child_idx))
+ if (is_objc_pointer) {
+ lldb::ValueObjectSP synthetic = base->GetSyntheticValue();
+ if (!synthetic || synthetic == base) {
+ std::string err_msg =
+ llvm::formatv("\"({0}) {1}\" is not an array type",
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation());
+ }
+ if (static_cast<uint32_t>(child_idx) >=
+ synthetic->GetNumChildrenIgnoringErrors()) {
+ std::string err_msg = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation());
+ }
+ child_valobj_sp = synthetic->GetChildAtIndex(child_idx);
+ if (!child_valobj_sp) {
+ std::string err_msg = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation());
+ }
+ if (m_use_dynamic != lldb::eNoDynamicValues) {
+ if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic))
+ child_valobj_sp = std::move(dynamic_sp);
+ }
return child_valobj_sp;
- }
+ }
- auto base_type = base->GetCompilerType().GetNonReferenceType();
- if (!base_type.IsPointerType() && !base_type.IsArrayType())
- return llvm::make_error<DILDiagnosticError>(
- m_expr, "subscripted value is not an array or pointer",
- node->GetLocation());
- if (base_type.IsPointerToVoid())
- return llvm::make_error<DILDiagnosticError>(
- m_expr, "subscript of pointer to incomplete type 'void'",
- node->GetLocation());
+ child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true);
+ if (!child_valobj_sp) {
+ std::string err_msg = llvm::formatv(
+ "failed to use pointer as array for index {0} for "
+ "\"({1}) {2}\"",
+ child_idx, base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ if (base_type.IsPointerToVoid())
+ err_msg = "subscript of pointer to incomplete type 'void'";
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation());
+ }
+ } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) {
+ child_valobj_sp = base->GetChildAtIndex(child_idx);
+ if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic))
+ child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true);
+ if (!child_valobj_sp) {
+ std::string err_msg = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation());
+ }
+ } else if (base_type.IsScalarType()) {
+ child_valobj_sp =
+ base->GetSyntheticBitFieldChild(child_idx, child_idx, true);
+ if (!child_valobj_sp) {
+ std::string err_msg = llvm::formatv(
+ "bitfield range {0}-{1} is not valid for \"({2}) {3}\"", child_idx,
+ child_idx, base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation(), 1);
+ }
+ } else {
+ lldb::ValueObjectSP synthetic = base->GetSyntheticValue();
+ if (!m_use_synthetic || !synthetic || synthetic == base) {
+ std::string err_msg =
+ llvm::formatv("\"{0}\" is not an array type",
+ base->GetTypeName().AsCString("<invalid type>"));
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation(), 1);
+ }
+ if (static_cast<uint32_t>(child_idx) >=
+ synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) {
+ std::string err_msg = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation(), 1);
+ }
+ child_valobj_sp = synthetic->GetChildAtIndex(child_idx);
+ if (!child_valobj_sp) {
+ std::string err_msg = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ base->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetData());
+ return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg),
+ node->GetLocation(), 1);
+ }
+ }
- if (base_type.IsArrayType()) {
- if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx))
- return child_valobj_sp;
+ if (child_valobj_sp) {
+ if (m_use_dynamic != lldb::eNoDynamicValues) {
+ if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic))
+ child_valobj_sp = std::move(dynamic_sp);
+ }
+ return child_valobj_sp;
}
int64_t signed_child_idx = node->GetIndex();
@@ -402,4 +499,107 @@ Interpreter::Visit(const BitFieldExtractionNode *node) {
return child_valobj_sp;
}
+static llvm::Expected<lldb::TypeSystemSP>
+GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) {
+ SymbolContext symbol_context =
+ ctx->GetSymbolContext(lldb::eSymbolContextCompUnit);
+ lldb::LanguageType language = symbol_context.comp_unit->GetLanguage();
+
+ symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule);
+ return symbol_context.module_sp->GetTypeSystemForLanguage(language);
+}
+
+static CompilerType GetBasicType(lldb::TypeSystemSP type_system,
+ lldb::BasicType basic_type) {
+ if (type_system)
+ return type_system.get()->GetBasicTypeFromAST(basic_type);
+
+ return CompilerType();
+}
+
+llvm::Expected<CompilerType>
+Interpreter::PickIntegerType(lldb::TypeSystemSP type_system,
+ std::shared_ptr<ExecutionContextScope> ctx,
+ const IntegerLiteralNode *literal) {
+ // Binary, Octal, Hexadecimal and literals with a U suffix are allowed to be
+ // an unsigned integer.
+ bool unsigned_is_allowed = literal->IsUnsigned() || literal->GetRadix() != 10;
+ llvm::APInt apint = literal->GetValue();
+
+ llvm::SmallVector<std::pair<lldb::BasicType, lldb::BasicType>, 3> candidates;
+ if (literal->GetTypeSuffix() <= IntegerTypeSuffix::None)
+ candidates.emplace_back(lldb::eBasicTypeInt,
+ unsigned_is_allowed ? lldb::eBasicTypeUnsignedInt
+ : lldb::eBasicTypeInvalid);
+ if (literal->GetTypeSuffix() <= IntegerTypeSuffix::Long)
+ candidates.emplace_back(lldb::eBasicTypeLong,
+ unsigned_is_allowed ? lldb::eBasicTypeUnsignedLong
+ : lldb::eBasicTypeInvalid);
+ candidates.emplace_back(lldb::eBasicTypeLongLong,
+ lldb::eBasicTypeUnsignedLongLong);
+ for (auto [signed_, unsigned_] : candidates) {
+ CompilerType signed_type = type_system->GetBasicTypeFromAST(signed_);
+ if (!signed_type)
+ continue;
+ llvm::Expected<uint64_t> size = signed_type.GetBitSize(ctx.get());
+ if (!size)
+ return size.takeError();
+ if (!literal->IsUnsigned() && apint.isIntN(*size - 1))
+ return signed_type;
+ if (unsigned_ != lldb::eBasicTypeInvalid && apint.isIntN(*size))
+ return type_system->GetBasicTypeFromAST(unsigned_);
+ }
+
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr,
+ "integer literal is too large to be represented in any integer type",
+ literal->GetLocation());
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const IntegerLiteralNode *node) {
+ llvm::Expected<lldb::TypeSystemSP> type_system =
+ GetTypeSystemFromCU(m_exe_ctx_scope);
+ if (!type_system)
+ return type_system.takeError();
+
+ llvm::Expected<CompilerType> type =
+ PickIntegerType(*type_system, m_exe_ctx_scope, node);
+ if (!type)
+ return type.takeError();
+
+ Scalar scalar = node->GetValue();
+ // APInt from StringRef::getAsInteger comes with just enough bitwidth to
+ // hold the value. This adjusts APInt bitwidth to match the compiler type.
+ llvm::Expected<uint64_t> type_bitsize =
+ type->GetBitSize(m_exe_ctx_scope.get());
+ if (!type_bitsize)
+ return type_bitsize.takeError();
+ scalar.TruncOrExtendTo(*type_bitsize, false);
+ return ValueObject::CreateValueObjectFromScalar(m_target, scalar, *type,
+ "result");
+}
+
+llvm::Expected<lldb::ValueObjectSP>
+Interpreter::Visit(const FloatLiteralNode *node) {
+ llvm::Expected<lldb::TypeSystemSP> type_system =
+ GetTypeSystemFromCU(m_exe_ctx_scope);
+ if (!type_system)
+ return type_system.takeError();
+
+ bool isFloat =
+ &node->GetValue().getSemantics() == &llvm::APFloat::IEEEsingle();
+ lldb::BasicType basic_type =
+ isFloat ? lldb::eBasicTypeFloat : lldb::eBasicTypeDouble;
+ CompilerType type = GetBasicType(*type_system, basic_type);
+
+ if (!type)
+ return llvm::make_error<DILDiagnosticError>(
+ m_expr, "unable to create a const literal", node->GetLocation());
+
+ Scalar scalar = node->GetValue();
+ return ValueObject::CreateValueObjectFromScalar(m_target, scalar, type,
+ "result");
+}
+
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index eaefaf484bc1..0b2288a9d923 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -28,18 +28,23 @@ llvm::StringRef Token::GetTokenName(Kind kind) {
return "coloncolon";
case Kind::eof:
return "eof";
+ case Kind::float_constant:
+ return "float_constant";
case Kind::identifier:
return "identifier";
+ case Kind::integer_constant:
+ return "integer_constant";
case Kind::l_paren:
return "l_paren";
case Kind::l_square:
return "l_square";
case Kind::minus:
return "minus";
- case Kind::numeric_constant:
- return "numeric_constant";
case Kind::period:
return "period";
+ return "l_square";
+ case Kind::plus:
+ return "plus";
case Kind::r_paren:
return "r_paren";
case Kind::r_square:
@@ -70,13 +75,32 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr,
return candidate;
}
-static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); }
+static bool IsNumberBodyChar(char ch) {
+ return IsDigit(ch) || IsLetter(ch) || ch == '.';
+}
-static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr,
- llvm::StringRef &remainder) {
- if (IsDigit(remainder[0])) {
- llvm::StringRef number = remainder.take_while(IsNumberBodyChar);
- remainder = remainder.drop_front(number.size());
+static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder,
+ bool &isFloat) {
+ llvm::StringRef tail = remainder;
+ llvm::StringRef body = tail.take_while(IsNumberBodyChar);
+ size_t dots = body.count('.');
+ if (dots > 1 || dots == body.size())
+ return std::nullopt;
+ if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) {
+ isFloat = dots == 1;
+ tail = tail.drop_front(body.size());
+ bool isHex = body.contains_insensitive('x');
+ bool hasExp = !isHex && body.contains_insensitive('e');
+ bool hasHexExp = isHex && body.contains_insensitive('p');
+ if (hasExp || hasHexExp) {
+ isFloat = true; // This marks numbers like 0x1p1 and 1e1 as float
+ if (body.ends_with_insensitive("e") || body.ends_with_insensitive("p"))
+ if (tail.consume_front("+") || tail.consume_front("-"))
+ tail = tail.drop_while(IsNumberBodyChar);
+ }
+ size_t number_length = remainder.size() - tail.size();
+ llvm::StringRef number = remainder.take_front(number_length);
+ remainder = remainder.drop_front(number_length);
return number;
}
return std::nullopt;
@@ -106,18 +130,21 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr,
return Token(Token::eof, "", (uint32_t)expr.size());
uint32_t position = cur_pos - expr.begin();
- std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder);
- if (maybe_number)
- return Token(Token::numeric_constant, maybe_number->str(), position);
+ bool isFloat = false;
+ std::optional<llvm::StringRef> maybe_number = IsNumber(remainder, isFloat);
+ if (maybe_number) {
+ auto kind = isFloat ? Token::float_constant : Token::integer_constant;
+ return Token(kind, maybe_number->str(), position);
+ }
std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder);
if (maybe_word)
return Token(Token::identifier, maybe_word->str(), position);
constexpr std::pair<Token::Kind, const char *> operators[] = {
- {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
- {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"},
- {Token::period, "."}, {Token::r_paren, ")"}, {Token::r_square, "]"},
- {Token::star, "*"},
+ {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"},
+ {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"},
+ {Token::period, "."}, {Token::plus, "+"}, {Token::r_paren, ")"},
+ {Token::r_square, "]"}, {Token::star, "*"},
};
for (auto [kind, str] : operators) {
if (remainder.consume_front(str))
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index eac41fab9076..8c4f7fdb25be 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -179,10 +179,13 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
// Parse a primary_expression.
//
// primary_expression:
+// numeric_literal
// id_expression
// "(" expression ")"
//
ASTNodeUP DILParser::ParsePrimaryExpression() {
+ if (CurToken().IsOneOf({Token::integer_constant, Token::float_constant}))
+ return ParseNumericLiteral();
if (CurToken().IsOneOf(
{Token::coloncolon, Token::identifier, Token::l_paren})) {
// Save the source location for the diagnostics message.
@@ -346,6 +349,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc,
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
+// FIXME: Remove this once subscript operator uses ScalarLiteralNode.
// Parse a integer_literal.
//
// integer_literal:
@@ -370,6 +374,69 @@ std::optional<int64_t> DILParser::ParseIntegerConstant() {
return std::nullopt;
}
+// Parse a numeric_literal.
+//
+// numeric_literal:
+// ? Token::integer_constant ?
+// ? Token::floating_constant ?
+//
+ASTNodeUP DILParser::ParseNumericLiteral() {
+ ASTNodeUP numeric_constant;
+ if (CurToken().Is(Token::integer_constant))
+ numeric_constant = ParseIntegerLiteral();
+ else
+ numeric_constant = ParseFloatingPointLiteral();
+ if (!numeric_constant) {
+ BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}",
+ CurToken()),
+ CurToken().GetLocation(), CurToken().GetSpelling().length());
+ return std::make_unique<ErrorNode>();
+ }
+ m_dil_lexer.Advance();
+ return numeric_constant;
+}
+
+ASTNodeUP DILParser::ParseIntegerLiteral() {
+ Token token = CurToken();
+ auto spelling = token.GetSpelling();
+ llvm::StringRef spelling_ref = spelling;
+
+ auto radix = llvm::getAutoSenseRadix(spelling_ref);
+ IntegerTypeSuffix type = IntegerTypeSuffix::None;
+ bool is_unsigned = false;
+ if (spelling_ref.consume_back_insensitive("u"))
+ is_unsigned = true;
+ if (spelling_ref.consume_back_insensitive("ll"))
+ type = IntegerTypeSuffix::LongLong;
+ else if (spelling_ref.consume_back_insensitive("l"))
+ type = IntegerTypeSuffix::Long;
+ // Suffix 'u' can be only specified only once, before or after 'l'
+ if (!is_unsigned && spelling_ref.consume_back_insensitive("u"))
+ is_unsigned = true;
+
+ llvm::APInt raw_value;
+ if (!spelling_ref.getAsInteger(radix, raw_value))
+ return std::make_unique<IntegerLiteralNode>(token.GetLocation(), raw_value,
+ radix, is_unsigned, type);
+ return nullptr;
+}
+
+ASTNodeUP DILParser::ParseFloatingPointLiteral() {
+ Token token = CurToken();
+ auto spelling = token.GetSpelling();
+ llvm::StringRef spelling_ref = spelling;
+
+ llvm::APFloat raw_float(llvm::APFloat::IEEEdouble());
+ if (spelling_ref.consume_back_insensitive("f"))
+ raw_float = llvm::APFloat(llvm::APFloat::IEEEsingle());
+
+ auto StatusOrErr = raw_float.convertFromString(
+ spelling_ref, llvm::APFloat::rmNearestTiesToEven);
+ if (!errorToBool(StatusOrErr.takeError()))
+ return std::make_unique<FloatLiteralNode>(token.GetLocation(), raw_float);
+ return nullptr;
+}
+
void DILParser::Expect(Token::Kind kind) {
if (CurToken().IsNot(kind)) {
BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()),
diff --git a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
index 6fac1eba919b..9deebe29eaae 100644
--- a/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
+++ b/lldb/test/API/commands/command/script/add/TestAddParsedCommand.py
@@ -34,6 +34,7 @@ class ParsedCommandTestCase(TestBase):
else:
(short_opt, type, long_opt) = elem
substrs.append(f"-{short_opt} <{type}> ( --{long_opt} <{type}> )")
+
self.expect("help " + cmd_name, substrs=substrs)
def run_one_repeat(self, commands, expected_num_errors):
@@ -215,6 +216,19 @@ class ParsedCommandTestCase(TestBase):
"bool-arg (set: True): False",
"shlib-name (set: True): Something",
"disk-file-name (set: False):",
+ "flag-value (set: False):",
+ "line-num (set: False):",
+ "enum-option (set: False):",
+ ],
+ )
+ # Make sure flag values work:
+ self.expect(
+ "no-args -b false -s Something -f",
+ substrs=[
+ "bool-arg (set: True): False",
+ "shlib-name (set: True): Something",
+ "disk-file-name (set: False):",
+ "flag-value (set: True):",
"line-num (set: False):",
"enum-option (set: False):",
],
@@ -295,7 +309,7 @@ class ParsedCommandTestCase(TestBase):
# no-args turns off auto-repeat
results = self.run_one_repeat("no-args\n\n", 1)
- self.assertIn("No auto repeat", results, "Got auto-repeat error")
+ self.assertIn("no auto repeat", results, "Got auto-repeat error")
# one-args does the normal repeat
results = self.run_one_repeat("one-arg-no-opt ONE_ARG\n\n", 0)
diff --git a/lldb/test/API/commands/command/script/add/test_commands.py b/lldb/test/API/commands/command/script/add/test_commands.py
index b15ea935c058..db302796819a 100644
--- a/lldb/test/API/commands/command/script/add/test_commands.py
+++ b/lldb/test/API/commands/command/script/add/test_commands.py
@@ -16,10 +16,16 @@ class ReportingCmd(ParsedCommand):
if len(opt_def):
result.AppendMessage("Options:\n")
for long_option, elem in opt_def.items():
- dest = elem["dest"]
- result.AppendMessage(
- f"{long_option} (set: {elem['_value_set']}): {object.__getattribute__(self.get_parser(), dest)}\n"
- )
+ if "value_type" in elem:
+ print(f"Looking at {long_option} - {elem}")
+ dest = elem["dest"]
+ result.AppendMessage(
+ f"{long_option} (set: {elem['_value_set']}): {object.__getattribute__(self.get_parser(), dest)}\n"
+ )
+ else:
+ result.AppendMessage(
+ f"{long_option} (set: {elem['_value_set']}): flag value\n"
+ )
else:
result.AppendMessage("No options\n")
@@ -78,6 +84,8 @@ class NoArgsCommand(ReportingCmd):
default=None,
)
+ ov_parser.add_option("f", "flag-value", "This is a flag value")
+
ov_parser.add_option(
"l",
"line-num",
diff --git a/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
index a488276c2e54..ed028a1a4ea3 100644
--- a/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
@@ -11,7 +11,6 @@ class TestCase(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
index 38b8508b516d..0fb6e883597d 100644
--- a/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
@@ -11,7 +11,6 @@ class TestBasicDeque(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
index 85eaa8fe39ea..e631a8737637 100644
--- a/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
@@ -12,7 +12,6 @@ class TestDbgInfoContentDeque(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "18.0"])
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
index a3a409d678ee..1d0f9ccf18c6 100644
--- a/lldb/test/API/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/forward_list-dbg-info-content/TestDbgInfoContentForwardListFromStdModule.py
@@ -11,7 +11,6 @@ class TestDbgInfoContentForwardList(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
index c9f4a1588f39..a6ba0810e68e 100644
--- a/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
@@ -11,7 +11,6 @@ class TestBasicForwardList(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
index 5c82ac352abb..370c3674faec 100644
--- a/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
@@ -11,7 +11,6 @@ class TestCase(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
index 0ecc244412da..b26bd7dedda8 100644
--- a/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
@@ -13,7 +13,6 @@ class TestDbgInfoContentList(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "12.0"])
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
index f29f353ae079..6253a35e926d 100644
--- a/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
@@ -11,7 +11,6 @@ class TestBasicList(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
index 5e0ab48cfa5c..cc91ddcc538b 100644
--- a/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
+++ b/lldb/test/API/commands/expression/import-std-module/non-module-type-separation/TestNonModuleTypeSeparation.py
@@ -12,7 +12,6 @@ class TestCase(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
"""
This test is creating ValueObjects with both C++ module and debug
diff --git a/lldb/test/API/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
index 50419b6f761c..5bfdb9baf663 100644
--- a/lldb/test/API/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/shared_ptr-dbg-info-content/TestSharedPtrDbgInfoContentFromStdModule.py
@@ -11,7 +11,6 @@ class TestSharedPtrDbgInfoContent(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
index 9f04361c48d7..da86466018bb 100644
--- a/lldb/test/API/commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/shared_ptr/TestSharedPtrFromStdModule.py
@@ -10,9 +10,8 @@ from lldbsuite.test import lldbutil
class TestSharedPtr(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
- @skipIf(compiler="clang", compiler_version=["<", "17.0"])
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
+ @skipIf(compiler="clang", compiler_version=["<", "17.0"])
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py
index ba4df40bbbc5..1c32222e64f1 100644
--- a/lldb/test/API/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/vector-dbg-info-content/TestDbgInfoContentVectorFromStdModule.py
@@ -14,7 +14,6 @@ class TestDbgInfoContentVector(TestBase):
@skipIf(compiler="clang", compiler_version=["<", "12.0"])
@skipIf(macos_version=["<", "14.0"])
@skipIfDarwin # https://github.com/llvm/llvm-project/issues/106475
- @skipIfLinux
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
index 6fa9dd5b6f56..2cddce09aa8b 100644
--- a/lldb/test/API/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/vector-of-vectors/TestVectorOfVectorsFromStdModule.py
@@ -11,7 +11,6 @@ class TestVectorOfVectors(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
index 19d24c1bcb89..28edf1999362 100644
--- a/lldb/test/API/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/weak_ptr-dbg-info-content/TestDbgInfoContentWeakPtrFromStdModule.py
@@ -12,7 +12,6 @@ class TestDbgInfoContentWeakPtr(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "17.0"])
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
index e3cc9b92569c..f0a0a46a73e9 100644
--- a/lldb/test/API/commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/weak_ptr/TestWeakPtrFromStdModule.py
@@ -12,7 +12,6 @@ class TestSharedPtr(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "17.0"])
@skipIf(macos_version=["<", "15.0"])
- @skipUnlessDarwin
def test(self):
self.build()
diff --git a/lldb/test/API/commands/frame/select/TestFrameSelect.py b/lldb/test/API/commands/frame/select/TestFrameSelect.py
index cb10105c0fe7..9e21495d4b53 100644
--- a/lldb/test/API/commands/frame/select/TestFrameSelect.py
+++ b/lldb/test/API/commands/frame/select/TestFrameSelect.py
@@ -23,12 +23,12 @@ class TestFrameSelect(TestBase):
self.expect(
"frame select -r -1",
error=True,
- substrs=["Already at the bottom of the stack."],
+ substrs=["already at the bottom of the stack"],
)
self.expect(
"frame select -r -2147483647",
error=True,
- substrs=["Already at the bottom of the stack."],
+ substrs=["already at the bottom of the stack"],
)
self.expect(
"frame select -r -2147483648",
@@ -61,7 +61,7 @@ class TestFrameSelect(TestBase):
self.expect(
"frame select -r 1",
error=True,
- substrs=["Already at the top of the stack."],
+ substrs=["already at the top of the stack"],
)
@no_debug_info_test
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
index 0f5605718939..f47e86266f47 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -66,30 +66,27 @@ class TestFrameVarDILArraySubscript(TestBase):
self.expect(
"frame var 'int_arr[1.0]'",
error=True,
- substrs=["expected 'r_square', got: <'.'"],
+ substrs=["failed to parse integer constant: <'1.0' (float_constant)>"],
)
- # Base should be a "pointer to T" and index should be of an integral type.
- self.expect(
- "frame var 'idx_1[0]'",
- error=True,
- substrs=["subscripted value is not an array or pointer"],
- )
+ # Test accessing bits in scalar types.
+ self.expect_var_path("idx_1[0]", value="1")
+ self.expect_var_path("idx_1[1]", value="0")
+ self.expect_var_path("1[0]", value="1")
+
+ # Bit access not valid for a reference.
self.expect(
"frame var 'idx_1_ref[0]'",
error=True,
- substrs=["subscripted value is not an array or pointer"],
+ substrs=["bitfield range 0-0 is not valid"],
)
+
+ # Base should be a "pointer to T" and index should be of an integral type.
self.expect(
"frame var 'int_arr[int_ptr]'",
error=True,
substrs=["failed to parse integer constant"],
)
- self.expect(
- "frame var '1[2]'",
- error=True,
- substrs=["Unexpected token"],
- )
# Base should not be a pointer to void
self.expect(
@@ -105,6 +102,8 @@ class TestFrameVarDILArraySubscript(TestBase):
)
self.runCmd("settings set target.experimental.use-DIL true")
+ self.runCmd("script from myArraySynthProvider import *")
+ self.runCmd("type synth add -l myArraySynthProvider myArray")
# Test synthetic value subscription
self.expect_var_path("vector[1]", value="2")
@@ -113,3 +112,7 @@ class TestFrameVarDILArraySubscript(TestBase):
error=True,
substrs=["array index 100 is not valid"],
)
+ self.expect(
+ "frame var 'ma_ptr[0]'",
+ substrs=["(myArray) ma_ptr[0] = ([0] = 7, [1] = 8, [2] = 9, [3] = 10)"],
+ )
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp
index a9a3612dfae5..03ad3e872ca7 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp
@@ -1,5 +1,11 @@
#include <vector>
+class myArray {
+public:
+ int m_array[4] = {7, 8, 9, 10};
+ int m_arr_size = 4;
+};
+
int main(int argc, char **argv) {
int int_arr[] = {1, 2, 3};
int *int_ptr = int_arr;
@@ -29,5 +35,8 @@ int main(int argc, char **argv) {
std::vector<int> vector = {1, 2, 3};
+ myArray ma;
+ myArray *ma_ptr = &ma;
+
return 0; // Set a breakpoint here
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
new file mode 100644
index 000000000000..167899bd3907
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/myArraySynthProvider.py
@@ -0,0 +1,33 @@
+import lldb
+
+
+class myArraySynthProvider:
+ def __init__(self, valobj, dict):
+ self.valobj = valobj
+
+ def num_children(self):
+ size_valobj = self.valobj.GetChildMemberWithName("m_arr_size")
+ if size_valobj:
+ return size_valobj.GetValueAsUnsigned(0)
+ return 0
+
+ def get_child_at_index(self, index):
+ size_valobj = self.valobj.GetChildMemberWithName("m_arr_size")
+ arr = self.valobj.GetChildMemberWithName("m_array")
+ if not size_valobj or not arr:
+ return None
+ max_idx = size_valobj.GetValueAsUnsigned(0)
+ if index >= max_idx:
+ return None
+ return arr.GetChildAtIndex(index)
+
+ def get_child_index(self, name):
+ if name == "[0]":
+ return 0
+ if name == "[1]":
+ return
+ if name == "[2]":
+ return 2
+ if name == "[3]":
+ return 3
+ return -1
diff --git a/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py
index 38c72131d797..28eba4f1a70b 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/Indirection/TestFrameVarDILIndirection.py
@@ -35,7 +35,7 @@ class TestFrameVarDILIndirection(TestBase):
self.expect(
"frame variable '*1'",
error=True,
- substrs=["Unexpected token: <'1' (numeric_constant)>"],
+ substrs=["dereference failed: not a pointer, reference or array type"],
)
self.expect(
"frame variable '*val'",
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile b/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py b/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py
new file mode 100644
index 000000000000..431ec2829bc7
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/TestFrameVarDILLiterals.py
@@ -0,0 +1,88 @@
+"""
+Test DIL literals.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+
+class TestFrameVarDILLiterals(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_literals(self):
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.runCmd("settings set target.experimental.use-DIL true")
+
+ # Check number literals parsing
+ self.expect_var_path("1.0", value="1", type="double")
+ self.expect_var_path("1.0f", value="1", type="float")
+ self.expect_var_path("0x1.2p+3f", value="9", type="float")
+ self.expect_var_path("1", value="1", type="int")
+ self.expect_var_path("1u", value="1", type="unsigned int")
+ self.expect_var_path("0b1l", value="1", type="long")
+ self.expect_var_path("01ul", value="1", type="unsigned long")
+ self.expect_var_path("01lu", value="1", type="unsigned long")
+ self.expect_var_path("0o1ll", value="1", type="long long")
+ self.expect_var_path("0x1ULL", value="1", type="unsigned long long")
+ self.expect_var_path("0x1llu", value="1", type="unsigned long long")
+ self.expect(
+ "frame var '1LLL'",
+ error=True,
+ substrs=["Failed to parse token as numeric-constant"],
+ )
+ self.expect(
+ "frame var '1ullu'",
+ error=True,
+ substrs=["Failed to parse token as numeric-constant"],
+ )
+
+ # Check integer literal type edge cases (dil::Interpreter::PickIntegerType)
+ frame = thread.GetFrameAtIndex(0)
+ v = frame.GetValueForVariablePath("argc")
+ # Creating an SBType from a BasicType still requires any value from the frame
+ int_size = v.GetType().GetBasicType(lldb.eBasicTypeInt).GetByteSize()
+ long_size = v.GetType().GetBasicType(lldb.eBasicTypeLong).GetByteSize()
+ longlong_size = v.GetType().GetBasicType(lldb.eBasicTypeLongLong).GetByteSize()
+
+ longlong_str = "0x" + "F" * longlong_size * 2
+ longlong_str = str(int(longlong_str, 16))
+ self.assert_literal_type(frame, longlong_str, lldb.eBasicTypeUnsignedLongLong)
+ toolong_str = "0x" + "F" * longlong_size * 2 + "F"
+ self.expect(
+ f"frame var '{toolong_str}'",
+ error=True,
+ substrs=[
+ "integer literal is too large to be represented in any integer type"
+ ],
+ )
+
+ # These check only apply if adjacent types have different sizes
+ if int_size < long_size:
+ # For exmaple, 0xFFFFFFFF and 4294967295 will have different types
+ # even though the numeric value is the same
+ hex_str = "0x" + "F" * int_size * 2
+ dec_str = str(int(hex_str, 16))
+ self.assert_literal_type(frame, hex_str, lldb.eBasicTypeUnsignedInt)
+ self.assert_literal_type(frame, dec_str, lldb.eBasicTypeLong)
+ long_str = "0x" + "F" * int_size * 2 + "F"
+ ulong_str = long_str + "u"
+ self.assert_literal_type(frame, long_str, lldb.eBasicTypeLong)
+ self.assert_literal_type(frame, ulong_str, lldb.eBasicTypeUnsignedLong)
+ if long_size < longlong_size:
+ longlong_str = "0x" + "F" * long_size * 2 + "F"
+ ulonglong_str = longlong_str + "u"
+ self.assert_literal_type(frame, longlong_str, lldb.eBasicTypeLongLong)
+ self.assert_literal_type(
+ frame, ulonglong_str, lldb.eBasicTypeUnsignedLongLong
+ )
+
+ def assert_literal_type(self, frame, literal, expected_type):
+ value = frame.GetValueForVariablePath(literal)
+ basic_type = value.GetType().GetBasicType()
+ self.assertEqual(basic_type, expected_type)
diff --git a/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp b/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp
new file mode 100644
index 000000000000..c9bd8afb0d71
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/expr/Literals/main.cpp
@@ -0,0 +1,3 @@
+int main(int argc, char **argv) {
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index e9ee8b8661e5..c8527abf3c84 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -1,6 +1,7 @@
import json
import os
import re
+import shutil
import lldb
from lldbsuite.test.decorators import *
@@ -179,6 +180,7 @@ class TestCase(TestBase):
"totalDebugInfoParseTime",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
+ "totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
if self.getPlatform() != "windows":
@@ -291,6 +293,7 @@ class TestCase(TestBase):
"totalDebugInfoParseTime",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
+ "totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
stats = debug_stats["targets"][0]
@@ -331,6 +334,7 @@ class TestCase(TestBase):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
+ "totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -385,6 +389,7 @@ class TestCase(TestBase):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
+ "totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
stats = debug_stats["targets"][0]
@@ -407,6 +412,7 @@ class TestCase(TestBase):
"symbolTableSavedToCache",
"dwoFileCount",
"loadedDwoFileCount",
+ "dwoErrorCount",
"triple",
"uuid",
]
@@ -497,6 +503,7 @@ class TestCase(TestBase):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
+ "totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
target_stats = debug_stats["targets"][0]
@@ -655,6 +662,99 @@ class TestCase(TestBase):
self.assertEqual(debug_stats["totalDwoFileCount"], 2)
self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
+ @add_test_categories(["dwo"])
+ def test_dwo_missing_error_stats(self):
+ """
+ Test that DWO missing errors are reported correctly in statistics.
+ This test:
+ 1) Builds a program with split DWARF (.dwo files)
+ 2) Delete one of the two .dwo files
+ 3) Verify that 1 DWO error is reported in statistics
+ """
+ da = {
+ "CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
+ "EXE": self.getBuildArtifact("a.out"),
+ }
+ # -gsplit-dwarf creates separate .dwo files,
+ # Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
+ self.build(dictionary=da, debug_info="dwo")
+ exe = self.getBuildArtifact("a.out")
+
+ expected_dwo_files = [
+ self.getBuildArtifact("dwo_error_main.dwo"),
+ self.getBuildArtifact("dwo_error_foo.dwo"),
+ ]
+
+ # Verify expected files exist
+ for dwo_file in expected_dwo_files:
+ self.assertTrue(
+ os.path.exists(dwo_file),
+ f"Expected .dwo file does not exist: {dwo_file}",
+ )
+
+ # Remove one of .dwo files to trigger DWO load error
+ dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")
+ os.remove(dwo_main_file)
+
+ target = self.createTestTarget(file_path=exe)
+ debug_stats = self.get_stats()
+
+ # Check DWO load error statistics are reported
+ self.assertIn("totalDwoErrorCount", debug_stats)
+ self.assertEqual(debug_stats["totalDwoErrorCount"], 1)
+
+ # Since there's only one module, module stats should have the same count as total count
+ self.assertIn("dwoErrorCount", debug_stats["modules"][0])
+ self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)
+
+ @add_test_categories(["dwo"])
+ def test_dwo_id_mismatch_error_stats(self):
+ """
+ Test that DWO ID mismatch errors are reported correctly in statistics.
+ This test:
+ 1) Builds a program with split DWARF (.dwo files)
+ 2) Replace one of the .dwo files with a mismatched one to cause a DWO ID mismatch error
+ 3) Verifies that a DWO error is reported in statistics
+ """
+ da = {
+ "CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
+ "EXE": self.getBuildArtifact("a.out"),
+ }
+ # -gsplit-dwarf creates separate .dwo files,
+ # Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
+ self.build(dictionary=da, debug_info="dwo")
+ exe = self.getBuildArtifact("a.out")
+
+ expected_dwo_files = [
+ self.getBuildArtifact("dwo_error_main.dwo"),
+ self.getBuildArtifact("dwo_error_foo.dwo"),
+ ]
+
+ # Verify expected files exist
+ for dwo_file in expected_dwo_files:
+ self.assertTrue(
+ os.path.exists(dwo_file),
+ f"Expected .dwo file does not exist: {dwo_file}",
+ )
+
+ # Replace one of the original .dwo file content with another one to trigger DWO ID mismatch error
+ dwo_foo_file = self.getBuildArtifact("dwo_error_foo.dwo")
+ dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")
+
+ shutil.copy(dwo_main_file, dwo_foo_file)
+
+ # Create a new target and get stats
+ target = self.createTestTarget(file_path=exe)
+ debug_stats = self.get_stats()
+
+ # Check that DWO load error statistics are reported
+ self.assertIn("totalDwoErrorCount", debug_stats)
+ self.assertEqual(debug_stats["totalDwoErrorCount"], 1)
+
+ # Since there's only one module, module stats should have the same count as total count
+ self.assertIn("dwoErrorCount", debug_stats["modules"][0])
+ self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)
+
@skipUnlessDarwin
@no_debug_info_test
def test_dsym_binary_has_symfile_in_stats(self):
diff --git a/lldb/test/API/commands/statistics/basic/dwo_error_foo.cpp b/lldb/test/API/commands/statistics/basic/dwo_error_foo.cpp
new file mode 100644
index 000000000000..41618bdcee95
--- /dev/null
+++ b/lldb/test/API/commands/statistics/basic/dwo_error_foo.cpp
@@ -0,0 +1,10 @@
+struct foo {
+ int x;
+ bool y;
+};
+
+void dwo_error_foo() {
+ foo f;
+ f.x = 1;
+ f.y = true;
+}
diff --git a/lldb/test/API/commands/statistics/basic/dwo_error_main.cpp b/lldb/test/API/commands/statistics/basic/dwo_error_main.cpp
new file mode 100644
index 000000000000..4f09bd74e1fd
--- /dev/null
+++ b/lldb/test/API/commands/statistics/basic/dwo_error_main.cpp
@@ -0,0 +1,5 @@
+void dwo_error_foo();
+int main() {
+ dwo_error_foo();
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/asan/TestMemoryHistory.py b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
index 66f6e3e7502c..8ae2d4a60d60 100644
--- a/lldb/test/API/functionalities/asan/TestMemoryHistory.py
+++ b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
@@ -2,7 +2,6 @@
Test that ASan memory history provider returns correct stack traces
"""
-
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
@@ -10,6 +9,7 @@ from lldbsuite.test import lldbplatform
from lldbsuite.test import lldbutil
from lldbsuite.test_event.build_exception import BuildError
+
class MemoryHistoryTestCase(TestBase):
@skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default
@expectedFailureNetBSD
@@ -94,6 +94,11 @@ class MemoryHistoryTestCase(TestBase):
)
self.check_traces()
+ if self.platformIsDarwin():
+ # Make sure we're not stopped in the sanitizer library but instead at the
+ # point of failure in the user-code.
+ self.assertEqual(self.frame().GetFunctionName(), "main")
+
# do the same using SB API
process = self.dbg.GetSelectedTarget().process
val = (
@@ -218,6 +223,11 @@ class MemoryHistoryTestCase(TestBase):
self.check_traces()
+ if self.platformIsDarwin():
+ # Make sure we're not stopped in the sanitizer library but instead at the
+ # point of failure in the user-code.
+ self.assertEqual(self.frame().GetFunctionName(), "main")
+
# make sure the 'memory history' command still works even when we're
# generating a report now
self.expect(
diff --git a/lldb/test/API/functionalities/asan/TestReportData.py b/lldb/test/API/functionalities/asan/TestReportData.py
index dd6834a01b80..c832436b0f44 100644
--- a/lldb/test/API/functionalities/asan/TestReportData.py
+++ b/lldb/test/API/functionalities/asan/TestReportData.py
@@ -2,7 +2,6 @@
Test the AddressSanitizer runtime support for report breakpoint and data extraction.
"""
-
import json
import lldb
from lldbsuite.test.decorators import *
@@ -10,6 +9,7 @@ from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
from lldbsuite.test_event.build_exception import BuildError
+
class AsanTestReportDataCase(TestBase):
@skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default
@expectedFailureNetBSD
@@ -67,6 +67,11 @@ class AsanTestReportDataCase(TestBase):
lldb.eStopReasonInstrumentation,
)
+ if self.platformIsDarwin():
+ # Make sure we're not stopped in the sanitizer library but instead at the
+ # point of failure in the user-code.
+ self.assertEqual(self.frame().GetFunctionName(), "main")
+
self.expect(
"bt",
"The backtrace should show the crashing line",
diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py
index e7c53729f209..45750c7ac081 100644
--- a/lldb/test/API/functionalities/completion/TestCompletion.py
+++ b/lldb/test/API/functionalities/completion/TestCompletion.py
@@ -676,8 +676,8 @@ class CommandLineCompletionTestCase(TestBase):
self.check_completion_with_desc(
"breakpoint set -",
[
- ["-h", "Set the breakpoint on exception catcH."],
- ["-w", "Set the breakpoint on exception throW."],
+ ["-h", "Set the breakpoint on exception catch."],
+ ["-w", "Set the breakpoint on exception throw."],
],
)
@@ -685,8 +685,8 @@ class CommandLineCompletionTestCase(TestBase):
self.check_completion_with_desc(
"breakpoint set --",
[
- ["--on-catch", "Set the breakpoint on exception catcH."],
- ["--on-throw", "Set the breakpoint on exception throW."],
+ ["--on-catch", "Set the breakpoint on exception catch."],
+ ["--on-throw", "Set the breakpoint on exception throw."],
],
)
@@ -694,8 +694,8 @@ class CommandLineCompletionTestCase(TestBase):
self.check_completion_with_desc(
"breakpoint set --on-",
[
- ["--on-catch", "Set the breakpoint on exception catcH."],
- ["--on-throw", "Set the breakpoint on exception throW."],
+ ["--on-catch", "Set the breakpoint on exception catch."],
+ ["--on-throw", "Set the breakpoint on exception throw."],
],
)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ranges/ref_view/TestDataFormatterStdRangesRefView.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ranges/ref_view/TestDataFormatterStdRangesRefView.py
index f135da70c7b4..9ba10575d6fe 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ranges/ref_view/TestDataFormatterStdRangesRefView.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/ranges/ref_view/TestDataFormatterStdRangesRefView.py
@@ -29,7 +29,6 @@ class StdRangesRefViewDataFormatterTestCase(TestBase):
def do_test(self):
"""Test that std::ranges::ref_view is formatted correctly when printed."""
- self.build()
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "Break here", lldb.SBFileSpec("main.cpp", False)
)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py
index fec20bae997e..6a27b5d2f078 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/TestDataFormatterStdString.py
@@ -80,6 +80,8 @@ class StdStringDataFormatterTestCase(TestBase):
'(%s::string) Q = "quite a long std::strin with lots of info inside it"'
% ns,
"(%s::string *) null_str = nullptr" % ns,
+ '(CustomString) custom_str = "hello!"',
+ '(CustomWString) custom_wstr = L"hello!"',
],
)
@@ -143,6 +145,10 @@ class StdStringDataFormatterTestCase(TestBase):
'(%s::u16string) u16_empty = u""' % ns,
'(%s::u32string) u32_string = U"🍄🍅🍆🍌"' % ns,
'(%s::u32string) u32_empty = U""' % ns,
+ '(CustomStringU16) custom_u16 = u"ß水氶"',
+ '(CustomStringU16) custom_u16_empty = u""',
+ '(CustomStringU32) custom_u32 = U"🍄🍅🍆🍌"',
+ '(CustomStringU32) custom_u32_empty = U""',
],
)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/main.cpp
index f22c890861d0..577b78e35fc1 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/string/main.cpp
@@ -1,6 +1,33 @@
+#include <cstdlib>
#include <stdint.h>
#include <string>
+template <typename T> struct CustomAlloc {
+ using value_type = T;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+ using size_type = std::size_t;
+
+ pointer allocate(size_type n) { return (T *)malloc(n * sizeof(T)); }
+
+ void deallocate(pointer p, size_type) {
+ if (p)
+ free(p);
+ }
+};
+
+using CustomString =
+ std::basic_string<char, std::char_traits<char>, CustomAlloc<char>>;
+
+using CustomWString =
+ std::basic_string<wchar_t, std::char_traits<wchar_t>, CustomAlloc<wchar_t>>;
+
+using CustomStringU16 = std::basic_string<char16_t, std::char_traits<char16_t>,
+ CustomAlloc<char16_t>>;
+
+using CustomStringU32 = std::basic_string<char32_t, std::char_traits<char32_t>,
+ CustomAlloc<char32_t>>;
+
size_t touch_string(std::string &in_str) {
return in_str.size(); // Break here to look at bad string
}
@@ -99,8 +126,16 @@ int main() {
std::string *pq = &q;
std::string *pQ = &Q;
+ CustomString custom_str("hello!");
+ CustomWString custom_wstr(L"hello!");
+ CustomStringU16 custom_u16(u16_string.c_str());
+ CustomStringU16 custom_u16_empty(u"");
+ CustomStringU32 custom_u32(u32_string.c_str());
+ CustomStringU32 custom_u32_empty(U"");
+
S.assign(L"!!!!!"); // Set break point at this line.
std::string *not_a_string = (std::string *)0x0;
touch_string(*not_a_string);
+
return 0;
}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py
index 1a23d9a19fe1..c3d51a49c3f5 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/TestDataFormatterLibcxxInvalidVectorSimulator.py
@@ -21,21 +21,21 @@ class LibcxxInvalidVectorDataFormatterSimulatorTestCase(TestBase):
self.expect(
"frame variable v1",
- substrs=["size=error: Invalid value for end of vector."],
+ substrs=["size=error: invalid value for end of vector"],
)
self.expect(
"frame variable v2",
- substrs=["size=error: Invalid value for start of vector."],
+ substrs=["size=error: invalid value for start of vector"],
)
self.expect(
"frame variable v3",
- substrs=["size=error: Start of vector data begins after end pointer."],
+ substrs=["size=error: start of vector data begins after end pointer"],
)
self.expect(
"frame variable v4",
- substrs=["size=error: Failed to determine start/end of vector data."],
+ substrs=["size=error: failed to determine start/end of vector data"],
)
self.expect(
"frame variable v5",
- substrs=["size=error: Size not multiple of element size."],
+ substrs=["size=error: size not multiple of element size"],
)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp
index f10811817c0d..5943b35deab8 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/invalid-vector/main.cpp
@@ -1,4 +1,4 @@
-#define COMPRESSED_PAIR_REV 3
+#define COMPRESSED_PAIR_REV 4
#include <libcxx-simulators-common/compressed_pair.h>
namespace std {
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py
index c8d9c2e389a0..f27fc2e3c456 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/TestDataFormatterLibcxxStringSimulator.py
@@ -28,7 +28,7 @@ class LibcxxStringDataFormatterSimulatorTestCase(TestBase):
for v in [None, "ALTERNATE_LAYOUT"]:
for r in range(6):
- for c in range(4):
+ for c in range(5):
name = "test_r%d_c%d" % (r, c)
defines = ["REVISION=%d" % r, "COMPRESSED_PAIR_REV=%d" % c]
if v:
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/main.cpp
index cf431e524069..b19c05159670 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/string/main.cpp
@@ -209,7 +209,7 @@ public:
__long &getLongRep() {
#if COMPRESSED_PAIR_REV == 0
return __r_.first().__l;
-#elif COMPRESSED_PAIR_REV <= 3
+#else
return __rep_.__l;
#endif
}
@@ -217,14 +217,14 @@ public:
__short &getShortRep() {
#if COMPRESSED_PAIR_REV == 0
return __r_.first().__s;
-#elif COMPRESSED_PAIR_REV <= 3
+#else
return __rep_.__s;
#endif
}
#if COMPRESSED_PAIR_REV == 0
std::__lldb::__compressed_pair<__rep, allocator_type> __r_;
-#elif COMPRESSED_PAIR_REV <= 3
+#else
_LLDB_COMPRESSED_PAIR(__rep, __rep_, allocator_type, __alloc_);
#endif
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py
index e623c3a1413b..1e25ac947203 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/TestDataFormatterLibcxxUniquePtrSimulator.py
@@ -26,7 +26,7 @@ class LibcxxUniquePtrDataFormatterSimulatorTestCase(TestBase):
)
-for r in range(4):
+for r in range(5):
name = "test_r%d" % r
defines = ["COMPRESSED_PAIR_REV=%d" % r]
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp
index 3d174a91cc26..bd840aaceffa 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx-simulators/unique_ptr/main.cpp
@@ -20,8 +20,7 @@ public:
std::__lldb::__compressed_pair<pointer, deleter_type> __ptr_;
explicit unique_ptr(pointer __p) noexcept
: __ptr_(__p, std::__lldb::__value_init_tag()) {}
-#elif COMPRESSED_PAIR_REV == 1 || COMPRESSED_PAIR_REV == 2 || \
- COMPRESSED_PAIR_REV == 3
+#else
_LLDB_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
explicit unique_ptr(pointer __p) noexcept : __ptr_(__p), __deleter_() {}
#endif
diff --git a/lldb/test/API/functionalities/disassembler-variables/Makefile b/lldb/test/API/functionalities/disassembler-variables/Makefile
new file mode 100644
index 000000000000..3d4bcd22fa65
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/Makefile
@@ -0,0 +1,32 @@
+# List all assembler inputs stable test fixtures.
+ASM_SOURCES := \
+ d_original_example.s \
+ regs_int_params.s \
+ regs_fp_params.s \
+ regs_mixed_params.s \
+ live_across_call.s \
+ loop_reg_rotate.s \
+ seed_reg_const_undef.s
+
+ASM_OBJS := $(ASM_SOURCES:.s=.o)
+
+# Provide a tiny dummy so the harness can link an exe without ASM_OBJS.
+C_SOURCES := dummy_main.c
+
+# Generating the dummy source on demand.
+dummy_main.c:
+ @echo 'int main(void){return 0;}' > $@
+
+# Assemble .s → .o using the configured compiler.
+%.o: %.s
+ $(CC) -c -x assembler $< -o $@
+
+# Default target: build all .o fixtures and the dummy exe.
+.PHONY: all
+all: $(ASM_OBJS) $(EXE)
+
+# Keeping things tidy.
+clean::
+ $(RM) -f $(ASM_OBJS) dummy_main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py
new file mode 100644
index 000000000000..f107efbdddde
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/TestVariableAnnotationsDisassembler.py
@@ -0,0 +1,118 @@
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+import lldb
+import os
+import re
+
+
+# Requires ELF assembler directives (.section … @progbits, .ident, etc.);
+# not compatible with COFF/Mach-O toolchains.
+@skipUnlessPlatform(["linux", "android", "freebsd", "netbsd"])
+class TestVariableAnnotationsDisassembler(TestBase):
+ def _build_obj(self, obj_name: str) -> str:
+ # Let the Makefile build all .o’s (pattern rule). Then grab the one we need.
+ self.build()
+ obj = self.getBuildArtifact(obj_name)
+ self.assertTrue(os.path.exists(obj), f"missing object: {obj}")
+ return obj
+
+ def _create_target(self, path):
+ target = self.dbg.CreateTarget(path)
+ self.assertTrue(target, f"failed to create target for {path}")
+ return target
+
+ def _disassemble_verbose_symbol(self, symname):
+ self.runCmd(f"disassemble -n {symname} -v", check=True)
+ return self.res.GetOutput()
+
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_d_original_example_O1(self):
+ obj = self._build_obj("d_original_example.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("main")
+ print(out)
+ self.assertIn("argc = ", out)
+ self.assertIn("argv = ", out)
+ self.assertIn("i = ", out)
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_regs_int_params(self):
+ obj = self._build_obj("regs_int_params.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_int_params")
+ print(out)
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg1\b|RDX\b)")
+ self.assertRegex(out, r"\bd\s*=\s*(DW_OP_reg2\b|RCX\b)")
+ self.assertRegex(out, r"\be\s*=\s*(DW_OP_reg8\b|R8\b)")
+ self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg9\b|R9\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_regs_fp_params(self):
+ obj = self._build_obj("regs_fp_params.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_fp_params")
+ print(out)
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg17\b|XMM0\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg18\b|XMM1\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg19\b|XMM2\b)")
+ self.assertRegex(out, r"\bd\s*=\s*(DW_OP_reg20\b|XMM3\b)")
+ self.assertRegex(out, r"\be\s*=\s*(DW_OP_reg21\b|XMM4\b)")
+ self.assertRegex(out, r"\bf\s*=\s*(DW_OP_reg22\b|XMM5\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_regs_mixed_params(self):
+ obj = self._build_obj("regs_mixed_params.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("regs_mixed_params")
+ print(out)
+ self.assertRegex(out, r"\ba\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertRegex(out, r"\bb\s*=\s*(DW_OP_reg4\b|RSI\b)")
+ self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg17\b|XMM0\b|DW_OP_reg\d+\b)")
+ self.assertRegex(out, r"\by\s*=\s*(DW_OP_reg18\b|XMM1\b|DW_OP_reg\d+\b)")
+ self.assertRegex(out, r"\bc\s*=\s*(DW_OP_reg1\b|RDX\b)")
+ self.assertRegex(out, r"\bz\s*=\s*(DW_OP_reg19\b|XMM2\b|DW_OP_reg\d+\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_live_across_call(self):
+ obj = self._build_obj("live_across_call.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("live_across_call")
+ print(out)
+ self.assertRegex(out, r"\bx\s*=\s*(DW_OP_reg5\b|RDI\b)")
+ self.assertIn("call", out)
+ self.assertRegex(out, r"\br\s*=\s*(DW_OP_reg0\b|RAX\b|DW_OP_reg\d+\b)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_loop_reg_rotate(self):
+ obj = self._build_obj("loop_reg_rotate.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("loop_reg_rotate")
+ print(out)
+ self.assertRegex(out, r"\bn\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bseed\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bk\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bj\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertRegex(out, r"\bi\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertNotIn("<decoding error>", out)
+
+ @no_debug_info_test
+ @skipIf(archs=no_match(["x86_64"]))
+ def test_seed_reg_const_undef(self):
+ obj = self._build_obj("seed_reg_const_undef.o")
+ target = self._create_target(obj)
+ out = self._disassemble_verbose_symbol("main")
+ print(out)
+ self.assertRegex(out, r"\b(i|argc)\s*=\s*(DW_OP_reg\d+\b|R[A-Z0-9]+)")
+ self.assertNotIn("<decoding error>", out)
diff --git a/lldb/test/API/functionalities/disassembler-variables/d_original_example.s b/lldb/test/API/functionalities/disassembler-variables/d_original_example.s
new file mode 100644
index 000000000000..c38742cfc683
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/d_original_example.s
@@ -0,0 +1,461 @@
+/* Original C (for context):
+* #include <stdio.h>
+*
+* int main(int argc, char **argv) {
+* for (int i = 1; i < argc; ++i)
+* puts(argv[i]);
+* return 0;
+* }
+*/
+ .file "d_original_example.c"
+ .text
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main,@function
+main: # @main
+.Lfunc_begin0:
+ .file 0 "." "d_original_example.c" md5 0x25192a1d5a6018cf37d369f9004fab00
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ #DEBUG_VALUE: i <- 1
+ .loc 0 4 21 prologue_end # d_original_example.c:4:21
+ cmpl $2, %edi
+.Ltmp0:
+ .loc 0 4 3 is_stmt 0 # d_original_example.c:4:3
+ jl .LBB0_4
+.Ltmp1:
+# %bb.1: # %for.body.preheader
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ #DEBUG_VALUE: i <- 1
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ pushq %r15
+ pushq %r14
+ pushq %rbx
+ pushq %rax
+ .cfi_offset %rbx, -40
+ .cfi_offset %r14, -32
+ .cfi_offset %r15, -24
+ movq %rsi, %rbx
+.Ltmp2:
+ .loc 0 4 21 # d_original_example.c:4:21
+ movl %edi, %r14d
+.Ltmp3:
+ #DEBUG_VALUE: main:argc <- $r14d
+ .loc 0 0 21 # d_original_example.c:0:21
+ movl $1, %r15d
+.Ltmp4:
+ .p2align 4
+.LBB0_2: # %for.body
+ # =>This Inner Loop Header: Depth=1
+ #DEBUG_VALUE: main:argc <- $r14d
+ #DEBUG_VALUE: main:argv <- $rbx
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 5 10 is_stmt 1 # d_original_example.c:5:10
+ movq (%rbx,%r15,8), %rdi
+ .loc 0 5 5 is_stmt 0 # d_original_example.c:5:5
+ callq puts@PLT
+.Ltmp5:
+ .loc 0 4 29 is_stmt 1 # d_original_example.c:4:29
+ incq %r15
+.Ltmp6:
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 4 21 is_stmt 0 # d_original_example.c:4:21
+ cmpq %r15, %r14
+.Ltmp7:
+ .loc 0 4 3 # d_original_example.c:4:3
+ jne .LBB0_2
+.Ltmp8:
+# %bb.3:
+ #DEBUG_VALUE: main:argc <- $r14d
+ #DEBUG_VALUE: main:argv <- $rbx
+ #DEBUG_VALUE: i <- $r15
+ .loc 0 0 3 # d_original_example.c:0:3
+ addq $8, %rsp
+ popq %rbx
+.Ltmp9:
+ #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi
+ popq %r14
+.Ltmp10:
+ #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi
+ popq %r15
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ .cfi_restore %rbx
+ .cfi_restore %r14
+ .cfi_restore %r15
+ .cfi_restore %rbp
+.Ltmp11:
+.LBB0_4: # %for.cond.cleanup
+ #DEBUG_VALUE: main:argc <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: main:argv <- [DW_OP_LLVM_entry_value 1] $rsi
+ .loc 0 6 3 is_stmt 1 # d_original_example.c:6:3
+ xorl %eax, %eax
+ retq
+.Ltmp12:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+ .file 1 "/usr/include" "stdio.h" md5 0xf31eefcc3f15835fc5a4023a625cf609
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 3 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 94 # super-register DW_OP_reg14
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 83 # DW_OP_reg3
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp8-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 95 # DW_OP_reg15
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 11 # DW_TAG_lexical_block
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 72 # DW_TAG_call_site
+ .byte 0 # DW_CHILDREN_no
+ .byte 127 # DW_AT_call_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 125 # DW_AT_call_return_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 5 # DW_FORM_data2
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 11 # Abbreviation Code
+ .byte 38 # DW_TAG_const_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x7f DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x38 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 110 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 110 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x3f:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 128 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x48:0x10 DW_TAG_lexical_block
+ .byte 0 # DW_AT_low_pc
+ .long .Ltmp8-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 5 # Abbrev [5] 0x4e:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 110 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 6 # Abbrev [6] 0x58:0x6 DW_TAG_call_site
+ .long 95 # DW_AT_call_origin
+ .byte 1 # DW_AT_call_return_pc
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x5f:0xf DW_TAG_subprogram
+ .byte 3 # DW_AT_name
+ .byte 1 # DW_AT_decl_file
+ .short 661 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 110 # DW_AT_type
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x68:0x5 DW_TAG_formal_parameter
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x6e:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x72:0x5 DW_TAG_pointer_type
+ .long 119 # DW_AT_type
+ .byte 11 # Abbrev [11] 0x77:0x5 DW_TAG_const_type
+ .long 124 # DW_AT_type
+ .byte 9 # Abbrev [9] 0x7c:0x4 DW_TAG_base_type
+ .byte 5 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x80:0x5 DW_TAG_pointer_type
+ .long 133 # DW_AT_type
+ .byte 10 # Abbrev [10] 0x85:0x5 DW_TAG_pointer_type
+ .long 124 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 44 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "d_original_example.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=140
+.Linfo_string3:
+ .asciz "puts" # string offset=142
+.Linfo_string4:
+ .asciz "int" # string offset=147
+.Linfo_string5:
+ .asciz "char" # string offset=151
+.Linfo_string6:
+ .asciz "main" # string offset=156
+.Linfo_string7:
+ .asciz "argc" # string offset=161
+.Linfo_string8:
+ .asciz "argv" # string offset=166
+.Linfo_string9:
+ .asciz "i" # string offset=171
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp5
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/live_across_call.s b/lldb/test/API/functionalities/disassembler-variables/live_across_call.s
new file mode 100644
index 000000000000..cd9f08afe6fd
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/live_across_call.s
@@ -0,0 +1,371 @@
+/* Original C (for context):
+* // Declare a real external call so the compiler must respect ABI clobbers.
+* extern int leaf(int) __attribute__((noinline));
+*
+* __attribute__((noinline))
+* int live_across_call(int x) {
+* volatile int a = x; // a starts in a GPR (from arg)
+* asm volatile("" :: "r"(a)); // keep 'a' live in a register
+* int r = leaf(a); // 'a' is live across the call
+* asm volatile("" :: "r"(a), "r"(r));// still live afterwards
+* return a + r;
+* }
+*/
+ .file "live_across_call.c"
+ .text
+ .globl live_across_call # -- Begin function live_across_call
+ .p2align 4
+ .type live_across_call,@function
+live_across_call: # @live_across_call
+.Lfunc_begin0:
+ .file 0 "." "live_across_call.c" md5 0x351c37295026edf0d468774d35f47e5e
+ .loc 0 5 0 # live_across_call.c:5:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: live_across_call:x <- $edi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $16, %rsp
+.Ltmp0:
+ .loc 0 6 16 prologue_end # live_across_call.c:6:16
+ movl %edi, -4(%rbp)
+ .loc 0 7 26 # live_across_call.c:7:26
+ movl -4(%rbp), %eax
+ .loc 0 7 3 is_stmt 0 # live_across_call.c:7:3
+ #APP
+ #NO_APP
+ .loc 0 8 16 is_stmt 1 # live_across_call.c:8:16
+ movl -4(%rbp), %edi
+.Ltmp1:
+ #DEBUG_VALUE: live_across_call:x <- [DW_OP_LLVM_entry_value 1] $edi
+ .loc 0 8 11 is_stmt 0 # live_across_call.c:8:11
+ callq leaf@PLT
+.Ltmp2:
+ #DEBUG_VALUE: live_across_call:r <- $eax
+ .loc 0 9 26 is_stmt 1 # live_across_call.c:9:26
+ movl -4(%rbp), %ecx
+ .loc 0 9 3 is_stmt 0 # live_across_call.c:9:3
+ #APP
+ #NO_APP
+ .loc 0 10 12 is_stmt 1 # live_across_call.c:10:12
+ addl -4(%rbp), %eax
+.Ltmp3:
+ .loc 0 10 3 epilogue_begin is_stmt 0 # live_across_call.c:10:3
+ addq $16, %rsp
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp4:
+.Lfunc_end0:
+ .size live_across_call, .Lfunc_end0-live_across_call
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 2 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp2-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 80 # super-register DW_OP_reg0
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 72 # DW_TAG_call_site
+ .byte 0 # DW_CHILDREN_no
+ .byte 127 # DW_AT_call_origin
+ .byte 19 # DW_FORM_ref4
+ .byte 125 # DW_AT_call_return_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 60 # DW_AT_declaration
+ .byte 25 # DW_FORM_flag_present
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 9 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 10 # Abbreviation Code
+ .byte 53 # DW_TAG_volatile_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x66 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x33 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 104 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 5 # DW_AT_decl_line
+ .long 104 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x3f:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 108 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x4a:0x9 DW_TAG_variable
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 8 # DW_AT_decl_line
+ .long 104 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x53:0x6 DW_TAG_call_site
+ .long 90 # DW_AT_call_origin
+ .byte 1 # DW_AT_call_return_pc
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x5a:0xe DW_TAG_subprogram
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 104 # DW_AT_type
+ # DW_AT_declaration
+ # DW_AT_external
+ .byte 8 # Abbrev [8] 0x62:0x5 DW_TAG_formal_parameter
+ .long 104 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 9 # Abbrev [9] 0x68:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 10 # Abbrev [10] 0x6c:0x5 DW_TAG_volatile_type
+ .long 104 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 40 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "live_across_call.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=138
+.Linfo_string3:
+ .asciz "leaf" # string offset=140
+.Linfo_string4:
+ .asciz "int" # string offset=145
+.Linfo_string5:
+ .asciz "live_across_call" # string offset=149
+.Linfo_string6:
+ .asciz "a" # string offset=166
+.Linfo_string7:
+ .asciz "x" # string offset=168
+.Linfo_string8:
+ .asciz "r" # string offset=170
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp2
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s
new file mode 100644
index 000000000000..c01e2b28fd2b
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/loop_reg_rotate.s
@@ -0,0 +1,557 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int loop_reg_rotate(int n, int seed) {
+* volatile int acc = seed; // keep as a named local
+* int i = 0, j = 1, k = 2; // extra pressure but not enough to spill
+*
+* for (int t = 0; t < n; ++t) {
+* // Mix uses so the allocator may reshuffle regs for 'acc'
+* acc = acc + i;
+* asm volatile("" :: "r"(acc)); // pin 'acc' live here
+* acc = acc ^ j;
+* asm volatile("" :: "r"(acc)); // and here
+* acc = acc + k;
+* i ^= acc; j += acc; k ^= j;
+* }
+*
+* asm volatile("" :: "r"(acc));
+* return acc + i + j + k;
+* }
+*/
+ .file "loop_reg_rotate.c"
+ .text
+ .globl loop_reg_rotate # -- Begin function loop_reg_rotate
+ .p2align 4
+ .type loop_reg_rotate,@function
+loop_reg_rotate: # @loop_reg_rotate
+.Lfunc_begin0:
+ .file 0 "." "loop_reg_rotate.c" md5 0x388f52de76e9442230e689fb9be1b4ef
+ .loc 0 2 0 # loop_reg_rotate.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 3 16 prologue_end # loop_reg_rotate.c:3:16
+ movl %esi, -4(%rbp)
+.Ltmp1:
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 6 21 # loop_reg_rotate.c:6:21
+ testl %edi, %edi
+.Ltmp2:
+ .loc 0 6 3 is_stmt 0 # loop_reg_rotate.c:6:3
+ jle .LBB0_1
+.Ltmp3:
+# %bb.3: # %for.body.preheader
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 0 3 # loop_reg_rotate.c:0:3
+ xorl %eax, %eax
+ movl $1, %edx
+ movl $2, %ecx
+.Ltmp4:
+ .p2align 4
+.LBB0_4: # %for.body
+ # =>This Inner Loop Header: Depth=1
+ #DEBUG_VALUE: loop_reg_rotate:n <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- [DW_OP_LLVM_entry_value 1] $esi
+ #DEBUG_VALUE: t <- [DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_minus, DW_OP_consts 18446744073709551615, DW_OP_div, DW_OP_stack_value] undef, undef
+ #DEBUG_VALUE: loop_reg_rotate:k <- $ecx
+ #DEBUG_VALUE: loop_reg_rotate:j <- $edx
+ #DEBUG_VALUE: loop_reg_rotate:i <- $eax
+ .loc 0 8 9 is_stmt 1 # loop_reg_rotate.c:8:9
+ addl %eax, -4(%rbp)
+ .loc 0 9 28 # loop_reg_rotate.c:9:28
+ movl -4(%rbp), %esi
+ .loc 0 9 5 is_stmt 0 # loop_reg_rotate.c:9:5
+ #APP
+ #NO_APP
+ .loc 0 10 9 is_stmt 1 # loop_reg_rotate.c:10:9
+ xorl %edx, -4(%rbp)
+ .loc 0 11 28 # loop_reg_rotate.c:11:28
+ movl -4(%rbp), %esi
+ .loc 0 11 5 is_stmt 0 # loop_reg_rotate.c:11:5
+ #APP
+ #NO_APP
+ .loc 0 12 9 is_stmt 1 # loop_reg_rotate.c:12:9
+ addl %ecx, -4(%rbp)
+ .loc 0 13 7 # loop_reg_rotate.c:13:7
+ xorl -4(%rbp), %eax
+.Ltmp5:
+ #DEBUG_VALUE: loop_reg_rotate:i <- $eax
+ .loc 0 13 17 is_stmt 0 # loop_reg_rotate.c:13:17
+ addl -4(%rbp), %edx
+.Ltmp6:
+ #DEBUG_VALUE: loop_reg_rotate:j <- $edx
+ .loc 0 13 27 # loop_reg_rotate.c:13:27
+ xorl %edx, %ecx
+.Ltmp7:
+ #DEBUG_VALUE: loop_reg_rotate:k <- $ecx
+ #DEBUG_VALUE: t <- [DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_minus, DW_OP_consts 18446744073709551615, DW_OP_div, DW_OP_consts 1, DW_OP_plus, DW_OP_stack_value] undef, undef
+ .loc 0 6 21 is_stmt 1 # loop_reg_rotate.c:6:21
+ decl %edi
+.Ltmp8:
+ .loc 0 6 3 is_stmt 0 # loop_reg_rotate.c:6:3
+ jne .LBB0_4
+ jmp .LBB0_2
+.Ltmp9:
+.LBB0_1:
+ #DEBUG_VALUE: loop_reg_rotate:n <- $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- $esi
+ #DEBUG_VALUE: loop_reg_rotate:i <- 0
+ #DEBUG_VALUE: loop_reg_rotate:j <- 1
+ #DEBUG_VALUE: loop_reg_rotate:k <- 2
+ #DEBUG_VALUE: t <- 0
+ .loc 0 0 3 # loop_reg_rotate.c:0:3
+ movl $2, %ecx
+ movl $1, %edx
+ xorl %eax, %eax
+.Ltmp10:
+.LBB0_2: # %for.cond.cleanup
+ #DEBUG_VALUE: loop_reg_rotate:n <- [DW_OP_LLVM_entry_value 1] $edi
+ #DEBUG_VALUE: loop_reg_rotate:seed <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 16 26 is_stmt 1 # loop_reg_rotate.c:16:26
+ movl -4(%rbp), %esi
+ .loc 0 16 3 is_stmt 0 # loop_reg_rotate.c:16:3
+ #APP
+ #NO_APP
+ .loc 0 17 14 is_stmt 1 # loop_reg_rotate.c:17:14
+ addl %edx, %eax
+ .loc 0 17 18 is_stmt 0 # loop_reg_rotate.c:17:18
+ addl %ecx, %eax
+ .loc 0 17 22 # loop_reg_rotate.c:17:22
+ addl -4(%rbp), %eax
+ .loc 0 17 3 epilogue_begin # loop_reg_rotate.c:17:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp11:
+.Lfunc_end0:
+ .size loop_reg_rotate, .Lfunc_end0-loop_reg_rotate
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 6 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+ .long .Ldebug_loc3-.Lloclists_table_base0
+ .long .Ldebug_loc4-.Lloclists_table_base0
+ .long .Ldebug_loc5-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 85 # super-register DW_OP_reg5
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp10-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 80 # super-register DW_OP_reg0
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc3:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 81 # super-register DW_OP_reg1
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 1 # 1
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc4:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 2 # 2
+ .byte 159 # DW_OP_stack_value
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp4-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp9-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 82 # super-register DW_OP_reg2
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp9-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp10-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 2 # 2
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc5:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp4-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 11 # DW_TAG_lexical_block
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 7 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 8 # Abbreviation Code
+ .byte 53 # DW_TAG_volatile_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x7d DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x58 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 127 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x3f:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x48:0xb DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 145
+ .byte 124
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 131 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x53:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x5c:0x9 DW_TAG_variable
+ .byte 3 # DW_AT_location
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x65:0x9 DW_TAG_variable
+ .byte 4 # DW_AT_location
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x6e:0x10 DW_TAG_lexical_block
+ .byte 1 # DW_AT_low_pc
+ .long .Ltmp9-.Ltmp1 # DW_AT_high_pc
+ .byte 5 # Abbrev [5] 0x74:0x9 DW_TAG_variable
+ .byte 5 # DW_AT_location
+ .byte 11 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 127 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 0 # End Of Children Mark
+ .byte 7 # Abbrev [7] 0x7f:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 8 # Abbrev [8] 0x83:0x5 DW_TAG_volatile_type
+ .long 127 # DW_AT_type
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 52 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "loop_reg_rotate.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=137
+.Linfo_string3:
+ .asciz "loop_reg_rotate" # string offset=139
+.Linfo_string4:
+ .asciz "int" # string offset=155
+.Linfo_string5:
+ .asciz "acc" # string offset=159
+.Linfo_string6:
+ .asciz "n" # string offset=163
+.Linfo_string7:
+ .asciz "seed" # string offset=165
+.Linfo_string8:
+ .asciz "i" # string offset=170
+.Linfo_string9:
+ .asciz "j" # string offset=172
+.Linfo_string10:
+ .asciz "k" # string offset=174
+.Linfo_string11:
+ .asciz "t" # string offset=176
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+ .quad .Ltmp1
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s
new file mode 100644
index 000000000000..502ab151e0c5
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_fp_params.s
@@ -0,0 +1,304 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* double regs_fp_params(double a, double b, double c, double d, double e, double f) {
+* asm volatile("" :: "x"(a), "x"(b), "x"(c), "x"(d), "x"(e), "x"(f));
+* return a + b + c + d + e + f;
+* }*/
+ .file "regs_fp_params.c"
+ .text
+ .globl regs_fp_params # -- Begin function regs_fp_params
+ .p2align 4
+ .type regs_fp_params,@function
+regs_fp_params: # @regs_fp_params
+.Lfunc_begin0:
+ .file 0 "." "regs_fp_params.c" md5 0xdd883927454b0ea1cdce7b3c16c6a643
+ .loc 0 2 0 # regs_fp_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_fp_params:a <- $xmm0
+ #DEBUG_VALUE: regs_fp_params:b <- $xmm1
+ #DEBUG_VALUE: regs_fp_params:c <- $xmm2
+ #DEBUG_VALUE: regs_fp_params:d <- $xmm3
+ #DEBUG_VALUE: regs_fp_params:e <- $xmm4
+ #DEBUG_VALUE: regs_fp_params:f <- $xmm5
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 3 3 prologue_end # regs_fp_params.c:3:3
+ #APP
+ #NO_APP
+ .loc 0 4 12 # regs_fp_params.c:4:12
+ addsd %xmm1, %xmm0
+.Ltmp1:
+ #DEBUG_VALUE: regs_fp_params:a <- [DW_OP_LLVM_entry_value 1] $xmm0
+ .loc 0 4 16 is_stmt 0 # regs_fp_params.c:4:16
+ addsd %xmm2, %xmm0
+ .loc 0 4 20 # regs_fp_params.c:4:20
+ addsd %xmm3, %xmm0
+ .loc 0 4 24 # regs_fp_params.c:4:24
+ addsd %xmm4, %xmm0
+ .loc 0 4 28 # regs_fp_params.c:4:28
+ addsd %xmm5, %xmm0
+ .loc 0 4 3 epilogue_begin # regs_fp_params.c:4:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size regs_fp_params, .Lfunc_end0-regs_fp_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 97 # DW_OP_reg17
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 97 # DW_OP_reg17
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x6b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4b DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 114 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x3f:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 98
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x49:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 99
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x53:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 100
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x5d:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 101
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x67:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 102
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x72:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 4 # DW_AT_encoding
+ .byte 8 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 48 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_fp_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=136
+.Linfo_string3:
+ .asciz "regs_fp_params" # string offset=138
+.Linfo_string4:
+ .asciz "double" # string offset=153
+.Linfo_string5:
+ .asciz "a" # string offset=160
+.Linfo_string6:
+ .asciz "b" # string offset=162
+.Linfo_string7:
+ .asciz "c" # string offset=164
+.Linfo_string8:
+ .asciz "d" # string offset=166
+.Linfo_string9:
+ .asciz "e" # string offset=168
+.Linfo_string10:
+ .asciz "f" # string offset=170
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s
new file mode 100644
index 000000000000..0b2a60e2b4d5
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_int_params.s
@@ -0,0 +1,312 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int regs_int_params(int a, int b, int c, int d, int e, int f) {
+* // Keep all params in regs; avoid spilling to the stack.
+* // The compiler will usually keep a..f in the 6 integer-arg regs.
+* asm volatile("" :: "r"(a), "r"(b), "r"(c), "r"(d), "r"(e), "r"(f));
+* return a + b + c + d + e + f;
+* }
+*/
+ .file "regs_int_params.c"
+ .text
+ .globl regs_int_params # -- Begin function regs_int_params
+ .p2align 4
+ .type regs_int_params,@function
+regs_int_params: # @regs_int_params
+.Lfunc_begin0:
+ .file 0 "." "regs_int_params.c" md5 0xcf39432098ab893043cc8b4606354bd2
+ .loc 0 2 0 # regs_int_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_int_params:a <- $edi
+ #DEBUG_VALUE: regs_int_params:b <- $esi
+ #DEBUG_VALUE: regs_int_params:c <- $edx
+ #DEBUG_VALUE: regs_int_params:d <- $ecx
+ #DEBUG_VALUE: regs_int_params:e <- $r8d
+ #DEBUG_VALUE: regs_int_params:f <- $r9d
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ # kill: def $r9d killed $r9d def $r9
+ # kill: def $r8d killed $r8d def $r8
+ # kill: def $ecx killed $ecx def $rcx
+ # kill: def $edx killed $edx def $rdx
+ # kill: def $esi killed $esi def $rsi
+ # kill: def $edi killed $edi def $rdi
+.Ltmp0:
+ .loc 0 5 3 prologue_end # regs_int_params.c:5:3
+ #APP
+ #NO_APP
+ .loc 0 6 12 # regs_int_params.c:6:12
+ addl %edi, %esi
+.Ltmp1:
+ #DEBUG_VALUE: regs_int_params:b <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 6 16 is_stmt 0 # regs_int_params.c:6:16
+ leal (%rdx,%rcx), %eax
+ .loc 0 6 20 # regs_int_params.c:6:20
+ addl %esi, %eax
+ .loc 0 6 28 # regs_int_params.c:6:28
+ addl %r8d, %eax
+ addl %r9d, %eax
+ .loc 0 6 3 epilogue_begin # regs_int_params.c:6:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size regs_int_params, .Lfunc_end0-regs_int_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x6b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4b DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 114 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x40:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x49:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 81
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x53:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 82
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x5d:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 88
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x67:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 89
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 114 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x72:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 48 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_int_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=137
+.Linfo_string3:
+ .asciz "regs_int_params" # string offset=139
+.Linfo_string4:
+ .asciz "int" # string offset=155
+.Linfo_string5:
+ .asciz "a" # string offset=159
+.Linfo_string6:
+ .asciz "b" # string offset=161
+.Linfo_string7:
+ .asciz "c" # string offset=163
+.Linfo_string8:
+ .asciz "d" # string offset=165
+.Linfo_string9:
+ .asciz "e" # string offset=167
+.Linfo_string10:
+ .asciz "f" # string offset=169
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s
new file mode 100644
index 000000000000..691180b42f24
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/regs_mixed_params.s
@@ -0,0 +1,375 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* double regs_mixed_params(int a, int b, double x, double y, int c, double z) {
+* // Keep everything live; avoid spills.
+* asm volatile("" :: "r"(a), "r"(b), "x"(x), "x"(y), "r"(c), "x"(z));
+* // Some mixing so values stay in regs long enough to annotate.
+* double r = (double)(a + b + c) + x + y + z;
+* asm volatile("" :: "x"(r), "r"(a), "x"(x));
+* return r;
+* }
+*/
+ .file "regs_mixed_params.c"
+ .file 0 "." "regs_mixed_params.c" md5 0x73c4bda40238ae460aaecb3a6a2603cf
+ .text
+ .globl regs_mixed_params # -- Begin function regs_mixed_params
+ .p2align 4
+ .type regs_mixed_params,@function
+regs_mixed_params: # @regs_mixed_params
+.Lfunc_begin0:
+ .loc 0 2 0 # regs_mixed_params.c:2:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: regs_mixed_params:a <- $edi
+ #DEBUG_VALUE: regs_mixed_params:b <- $esi
+ #DEBUG_VALUE: regs_mixed_params:x <- $xmm0
+ #DEBUG_VALUE: regs_mixed_params:y <- $xmm1
+ #DEBUG_VALUE: regs_mixed_params:c <- $edx
+ #DEBUG_VALUE: regs_mixed_params:z <- $xmm2
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 0 4 3 prologue_end # regs_mixed_params.c:4:3
+ #APP
+ #NO_APP
+ .loc 0 6 25 # regs_mixed_params.c:6:25
+ addl %edi, %esi
+.Ltmp1:
+ #DEBUG_VALUE: regs_mixed_params:b <- [DW_OP_LLVM_entry_value 1] $esi
+ .loc 0 6 29 is_stmt 0 # regs_mixed_params.c:6:29
+ addl %edx, %esi
+ .loc 0 6 14 # regs_mixed_params.c:6:14
+ cvtsi2sd %esi, %xmm4
+ .loc 0 6 34 # regs_mixed_params.c:6:34
+ movapd %xmm0, %xmm3
+ addsd %xmm4, %xmm3
+ .loc 0 6 38 # regs_mixed_params.c:6:38
+ addsd %xmm1, %xmm3
+ .loc 0 6 42 # regs_mixed_params.c:6:42
+ addsd %xmm2, %xmm3
+.Ltmp2:
+ #DEBUG_VALUE: regs_mixed_params:r <- $xmm3
+ .loc 0 7 3 is_stmt 1 # regs_mixed_params.c:7:3
+ #APP
+ #NO_APP
+ .loc 0 8 3 # regs_mixed_params.c:8:3
+ movapd %xmm3, %xmm0
+.Ltmp3:
+ #DEBUG_VALUE: regs_mixed_params:x <- [DW_OP_LLVM_entry_value 1] $xmm0
+ .loc 0 8 3 epilogue_begin is_stmt 0 # regs_mixed_params.c:8:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp4:
+.Lfunc_end0:
+ .size regs_mixed_params, .Lfunc_end0-regs_mixed_params
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 3 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+ .long .Ldebug_loc1-.Lloclists_table_base0
+ .long .Ldebug_loc2-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 84 # super-register DW_OP_reg4
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 84 # super-register DW_OP_reg4
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc1:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Lfunc_begin0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp3-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 97 # DW_OP_reg17
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp3-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 4 # Loc expr size
+ .byte 163 # DW_OP_entry_value
+ .byte 1 # 1
+ .byte 97 # DW_OP_reg17
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_loc2:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp2-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 100 # DW_OP_reg20
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x77 DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x4 DW_TAG_base_type
+ .byte 3 # DW_AT_name
+ .byte 4 # DW_AT_encoding
+ .byte 8 # DW_AT_byte_size
+ .byte 3 # Abbrev [3] 0x2b:0x53 DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 4 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 39 # DW_AT_type
+ # DW_AT_external
+ .byte 4 # Abbrev [4] 0x3a:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x44:0x9 DW_TAG_formal_parameter
+ .byte 0 # DW_AT_location
+ .byte 7 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x4d:0x9 DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x56:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 98
+ .byte 9 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x60:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 81
+ .byte 10 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 126 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x6a:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 99
+ .byte 11 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 2 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x74:0x9 DW_TAG_variable
+ .byte 2 # DW_AT_location
+ .byte 12 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 6 # DW_AT_decl_line
+ .long 39 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 2 # Abbrev [2] 0x7e:0x4 DW_TAG_base_type
+ .byte 6 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 56 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "regs_mixed_params.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=139
+.Linfo_string3:
+ .asciz "double" # string offset=141
+.Linfo_string4:
+ .asciz "regs_mixed_params" # string offset=148
+.Linfo_string5:
+ .asciz "a" # string offset=166
+.Linfo_string6:
+ .asciz "int" # string offset=168
+.Linfo_string7:
+ .asciz "b" # string offset=172
+.Linfo_string8:
+ .asciz "x" # string offset=174
+.Linfo_string9:
+ .asciz "y" # string offset=176
+.Linfo_string10:
+ .asciz "c" # string offset=178
+.Linfo_string11:
+ .asciz "z" # string offset=180
+.Linfo_string12:
+ .asciz "r" # string offset=182
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .long .Linfo_string9
+ .long .Linfo_string10
+ .long .Linfo_string11
+ .long .Linfo_string12
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s
new file mode 100644
index 000000000000..f85b8a712cbb
--- /dev/null
+++ b/lldb/test/API/functionalities/disassembler-variables/seed_reg_const_undef.s
@@ -0,0 +1,289 @@
+/* Original C (for context):
+* __attribute__((noinline))
+* int main(int argc, char **argv) {
+* int i = argc; // i in a reg (DW_OP_regN)
+* asm volatile("" :: "r"(i)); // keep i live here
+* i = 0; // i becomes const 0 (DW_OP_constu 0, stack_value)
+* asm volatile("" :: "r"(i)); // keep the const range materialized
+* return 0; // i ends -> <undef> after its range
+* }
+*/
+
+ .file "seed_reg_const_undef.c"
+ .text
+ .globl main # -- Begin function main
+ .p2align 4
+ .type main,@function
+main: # @main
+.Lfunc_begin0:
+ .file 0 "." "seed_reg_const_undef.c" md5 0x5e8dbf089d1bd72d395da802210b3138
+ .loc 0 3 0 # seed_reg_const_undef.c:3:0
+ .cfi_startproc
+# %bb.0: # %entry
+ #DEBUG_VALUE: main:argc <- $edi
+ #DEBUG_VALUE: main:argv <- $rsi
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ #DEBUG_VALUE: main:i <- $edi
+ .loc 0 5 3 prologue_end # seed_reg_const_undef.c:5:3
+ #APP
+ #NO_APP
+.Ltmp1:
+ #DEBUG_VALUE: main:i <- 0
+ .loc 0 7 3 # seed_reg_const_undef.c:7:3
+ xorl %eax, %eax
+ #APP
+ #NO_APP
+ .loc 0 8 3 # seed_reg_const_undef.c:8:3
+ xorl %eax, %eax
+ .loc 0 8 3 epilogue_begin is_stmt 0 # seed_reg_const_undef.c:8:3
+ popq %rbp
+ .cfi_def_cfa %rsp, 8
+ retq
+.Ltmp2:
+.Lfunc_end0:
+ .size main, .Lfunc_end0-main
+ .cfi_endproc
+ # -- End function
+ .section .debug_loclists,"",@progbits
+ .long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
+.Ldebug_list_header_start0:
+ .short 5 # Version
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+ .long 1 # Offset entry count
+.Lloclists_table_base0:
+ .long .Ldebug_loc0-.Lloclists_table_base0
+.Ldebug_loc0:
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp0-.Lfunc_begin0 # starting offset
+ .uleb128 .Ltmp1-.Lfunc_begin0 # ending offset
+ .byte 1 # Loc expr size
+ .byte 85 # super-register DW_OP_reg5
+ .byte 4 # DW_LLE_offset_pair
+ .uleb128 .Ltmp1-.Lfunc_begin0 # starting offset
+ .uleb128 .Lfunc_end0-.Lfunc_begin0 # ending offset
+ .byte 3 # Loc expr size
+ .byte 17 # DW_OP_consts
+ .byte 0 # 0
+ .byte 159 # DW_OP_stack_value
+ .byte 0 # DW_LLE_end_of_list
+.Ldebug_list_header_end0:
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 37 # DW_AT_producer
+ .byte 37 # DW_FORM_strx1
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 114 # DW_AT_str_offsets_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 37 # DW_FORM_strx1
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 115 # DW_AT_addr_base
+ .byte 23 # DW_FORM_sec_offset
+ .ascii "\214\001" # DW_AT_loclists_base
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 46 # DW_TAG_subprogram
+ .byte 1 # DW_CHILDREN_yes
+ .byte 17 # DW_AT_low_pc
+ .byte 27 # DW_FORM_addrx
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 64 # DW_AT_frame_base
+ .byte 24 # DW_FORM_exprloc
+ .byte 122 # DW_AT_call_all_calls
+ .byte 25 # DW_FORM_flag_present
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 39 # DW_AT_prototyped
+ .byte 25 # DW_FORM_flag_present
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 3 # Abbreviation Code
+ .byte 5 # DW_TAG_formal_parameter
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 4 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 2 # DW_AT_location
+ .byte 34 # DW_FORM_loclistx
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 5 # Abbreviation Code
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 37 # DW_FORM_strx1
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 6 # Abbreviation Code
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+ .short 5 # DWARF version number
+ .byte 1 # DWARF Unit Type
+ .byte 8 # Address Size (in bytes)
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 1 # Abbrev [1] 0xc:0x5b DW_TAG_compile_unit
+ .byte 0 # DW_AT_producer
+ .short 29 # DW_AT_language
+ .byte 1 # DW_AT_name
+ .long .Lstr_offsets_base0 # DW_AT_str_offsets_base
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .byte 2 # DW_AT_comp_dir
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .long .Laddr_table_base0 # DW_AT_addr_base
+ .long .Lloclists_table_base0 # DW_AT_loclists_base
+ .byte 2 # Abbrev [2] 0x27:0x2d DW_TAG_subprogram
+ .byte 0 # DW_AT_low_pc
+ .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc
+ .byte 1 # DW_AT_frame_base
+ .byte 86
+ # DW_AT_call_all_calls
+ .byte 3 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ # DW_AT_prototyped
+ .long 84 # DW_AT_type
+ # DW_AT_external
+ .byte 3 # Abbrev [3] 0x36:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 85
+ .byte 5 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 84 # DW_AT_type
+ .byte 3 # Abbrev [3] 0x40:0xa DW_TAG_formal_parameter
+ .byte 1 # DW_AT_location
+ .byte 84
+ .byte 6 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .long 88 # DW_AT_type
+ .byte 4 # Abbrev [4] 0x4a:0x9 DW_TAG_variable
+ .byte 0 # DW_AT_location
+ .byte 8 # DW_AT_name
+ .byte 0 # DW_AT_decl_file
+ .byte 4 # DW_AT_decl_line
+ .long 84 # DW_AT_type
+ .byte 0 # End Of Children Mark
+ .byte 5 # Abbrev [5] 0x54:0x4 DW_TAG_base_type
+ .byte 4 # DW_AT_name
+ .byte 5 # DW_AT_encoding
+ .byte 4 # DW_AT_byte_size
+ .byte 6 # Abbrev [6] 0x58:0x5 DW_TAG_pointer_type
+ .long 93 # DW_AT_type
+ .byte 6 # Abbrev [6] 0x5d:0x5 DW_TAG_pointer_type
+ .long 98 # DW_AT_type
+ .byte 5 # Abbrev [5] 0x62:0x4 DW_TAG_base_type
+ .byte 7 # DW_AT_name
+ .byte 6 # DW_AT_encoding
+ .byte 1 # DW_AT_byte_size
+ .byte 0 # End Of Children Mark
+.Ldebug_info_end0:
+ .section .debug_str_offsets,"",@progbits
+ .long 40 # Length of String Offsets Set
+ .short 5
+ .short 0
+.Lstr_offsets_base0:
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)" # string offset=0
+.Linfo_string1:
+ .asciz "seed_reg_const_undef.c" # string offset=119
+.Linfo_string2:
+ .asciz "." # string offset=142
+.Linfo_string3:
+ .asciz "main" # string offset=144
+.Linfo_string4:
+ .asciz "int" # string offset=149
+.Linfo_string5:
+ .asciz "argc" # string offset=153
+.Linfo_string6:
+ .asciz "argv" # string offset=158
+.Linfo_string7:
+ .asciz "char" # string offset=163
+.Linfo_string8:
+ .asciz "i" # string offset=168
+ .section .debug_str_offsets,"",@progbits
+ .long .Linfo_string0
+ .long .Linfo_string1
+ .long .Linfo_string2
+ .long .Linfo_string3
+ .long .Linfo_string4
+ .long .Linfo_string5
+ .long .Linfo_string6
+ .long .Linfo_string7
+ .long .Linfo_string8
+ .section .debug_addr,"",@progbits
+ .long .Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+ .short 5 # DWARF version number
+ .byte 8 # Address size
+ .byte 0 # Segment selector size
+.Laddr_table_base0:
+ .quad .Lfunc_begin0
+.Ldebug_addr_end0:
+ .ident "clang version 22.0.0git (https://github.com/UltimateForce21/llvm-project.git 79c0a9e1e7da0f727c41d27c9c6ff8a28bb7d06f)"
+ .section ".note.GNU-stack","",@progbits
+ .addrsig
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py b/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py
index a1adc20e864b..629361c6ae5b 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestqOffsets.py
@@ -12,6 +12,8 @@ class TestqOffsets(GDBRemoteTestBase):
def test(self):
self.server.responder = TestqOffsets.Responder()
+ # This ensures that we do not pick up any binaries on the host.
+ self.runCmd("platform select remote-linux")
target = self.createTarget("qOffsets.yaml")
text = target.modules[0].FindSection(".text")
self.assertEqual(text.GetLoadAddress(target), lldb.LLDB_INVALID_ADDRESS)
diff --git a/lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py b/lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py
index 774c61ad24d4..15bef0153ed0 100644
--- a/lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py
+++ b/lldb/test/API/functionalities/inferior-crashing/TestInferiorCrashingStep.py
@@ -201,7 +201,7 @@ class CrashingInferiorStepTestCase(TestBase):
self.expect("next", substrs=["Process", expected_state])
if expected_state == "exited":
- self.expect("thread list", error=True, substrs=["Process must be launched"])
+ self.expect("thread list", error=True, substrs=["process must be launched"])
else:
self.check_stop_reason()
diff --git a/lldb/test/API/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferiorStep.py b/lldb/test/API/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferiorStep.py
index 1464f2ef8664..1ad4c7902494 100644
--- a/lldb/test/API/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferiorStep.py
+++ b/lldb/test/API/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferiorStep.py
@@ -98,7 +98,7 @@ class CrashingRecursiveInferiorStepTestCase(TestBase):
self.expect("next", substrs=["Process", expected_state])
if expected_state == "exited":
- self.expect("thread list", error=True, substrs=["Process must be launched"])
+ self.expect("thread list", error=True, substrs=["process must be launched"])
else:
self.check_stop_reason()
diff --git a/lldb/test/API/functionalities/multiword-commands/TestMultiWordCommands.py b/lldb/test/API/functionalities/multiword-commands/TestMultiWordCommands.py
index c34fd3f3892a..0fa3254127e0 100644
--- a/lldb/test/API/functionalities/multiword-commands/TestMultiWordCommands.py
+++ b/lldb/test/API/functionalities/multiword-commands/TestMultiWordCommands.py
@@ -26,5 +26,5 @@ class MultiwordCommandsTestCase(TestBase):
self.expect(
'platform ""',
error=True,
- substrs=["Need to specify a non-empty subcommand."],
+ substrs=["need to specify a non-empty subcommand"],
)
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
index a68175dc4e4d..e9403b56ae19 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
@@ -696,6 +696,43 @@ class LinuxCoreTestCase(TestBase):
self.expect("register read --all")
+ @skipIfLLVMTargetMissing("ARM")
+ def test_arm_core_vfp(self):
+ # check reading VFP registers
+ target = self.dbg.CreateTarget(None)
+ self.assertTrue(target, VALID_TARGET)
+ process = target.LoadCore("linux-arm-vfp.core")
+
+ values = {
+ "d0": "0.5",
+ "d1": "1.5",
+ "d14": "14.5",
+ "d15": "15.5",
+ "s4": "4.5",
+ "s5": "5.5",
+ "s6": "6.5",
+ "s7": "7.5",
+ "fpscr": "0x20000000",
+ # s0 and s1 overlap d0, s2 and s3 overlap d1 and so on. Therefore,
+ # the following values are not as neat as those in the explicitly
+ # set registers.
+ "s0": "0",
+ "s1": "1.75",
+ "s2": "0",
+ "s3": "1.9375",
+ "s28": "0",
+ "s29": "2.703125",
+ "s30": "0",
+ "s31": "2.734375",
+ }
+ for regname, value in values.items():
+ self.expect(
+ "register read {}".format(regname),
+ substrs=["{} = {}".format(regname, value)],
+ )
+
+ self.expect("register read --all")
+
@skipIfLLVMTargetMissing("RISCV")
def test_riscv64_regs_gpr_fpr(self):
# check basic registers using 64 bit RISC-V core file
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py b/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py
index 020a226924ea..497b8e8a19a8 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/gcore/TestGCore.py
@@ -37,7 +37,7 @@ class GCoreTestCase(TestBase):
for thread in process:
reason = thread.GetStopReason()
self.assertStopReason(reason, lldb.eStopReasonSignal)
- signal = thread.GetStopReasonDataAtIndex(1)
+ signal = thread.GetStopReasonDataAtIndex(0)
# Check we got signal 19 (SIGSTOP)
self.assertEqual(signal, 19)
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.c b/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.c
new file mode 100644
index 000000000000..afc1dce04f9a
--- /dev/null
+++ b/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.c
@@ -0,0 +1,24 @@
+// linux-arm-vfp.core was generated with:
+// > gcc-12 -march=armv7+fp -nostdlib -static -Wl,--build-id=none \
+// linux-arm-vfp.c -o linux-arm-vfp.out
+// > ulimit -c 1000
+// > ulimit -s 8
+// > env -i ./linux-arm-vfp.out
+
+static void foo(char *boom) {
+ asm volatile(R"(
+ vmov.f64 d0, #0.5
+ vmov.f64 d1, #1.5
+ vmov.f64 d14, #14.5
+ vmov.f64 d15, #15.5
+ vmov.f32 s4, #4.5
+ vmov.f32 s5, #5.5
+ vmov.f32 s6, #6.5
+ vmov.f32 s7, #7.5
+ vcmp.f32 s7, s6
+ )");
+
+ *boom = 47;
+}
+
+void _start(void) { foo(0); }
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.core b/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.core
new file mode 100644
index 000000000000..217ba18fdf6d
--- /dev/null
+++ b/lldb/test/API/functionalities/postmortem/elf-core/linux-arm-vfp.core
Binary files differ
diff --git a/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py b/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py
index 4a848d1c2eb9..6d9aef286a79 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/thread_crash/TestLinuxCoreThreads.py
@@ -91,7 +91,7 @@ class LinuxCoreThreadsTestCase(TestBase):
reason = thread.GetStopReason()
if thread.GetThreadID() == tid:
self.assertStopReason(reason, lldb.eStopReasonSignal)
- signal = thread.GetStopReasonDataAtIndex(1)
+ signal = thread.GetStopReasonDataAtIndex(0)
# Check we got signal 4 (SIGILL)
self.assertEqual(signal, 4)
else:
diff --git a/lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py b/lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py
index 74e7c895c0fa..19edaac964e6 100644
--- a/lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py
+++ b/lldb/test/API/functionalities/rerun_and_expr_dylib/TestRerunAndExprDylib.py
@@ -20,14 +20,15 @@ def isUbuntu18_04():
with open(path) as f:
contents = f.read()
if "Ubuntu 18.04" in contents:
- return True
+ return "Ubuntu 18.04 is not supported."
- return False
+ return None
class TestRerunExprDylib(TestBase):
@skipTestIfFn(isUbuntu18_04, bugnumber="rdar://103831050")
@skipIfWindows
+ @skipIfRemote
def test(self):
"""
Tests whether re-launching a process without destroying
diff --git a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
index 9519c576689d..5916e62c44f2 100644
--- a/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
+++ b/lldb/test/API/functionalities/scripted_process/TestScriptedProcess.py
@@ -284,6 +284,6 @@ class ScriptedProcesTestCase(TestBase):
break
self.assertEqual(idx, int(reg.value, 16))
- self.assertTrue(frame.IsArtificial(), "Frame is not artificial")
+ self.assertTrue(frame.IsSynthetic(), "Frame is not synthetic")
pc = frame.GetPCAddress().GetLoadAddress(target_0)
self.assertEqual(pc, 0x0100001B00)
diff --git a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
index 2721d961bcb9..835267221ddb 100644
--- a/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
+++ b/lldb/test/API/functionalities/scripted_process/dummy_scripted_process.py
@@ -5,6 +5,7 @@ from typing import Any, Dict
import lldb
from lldb.plugins.scripted_process import ScriptedProcess
from lldb.plugins.scripted_process import ScriptedThread
+from lldb.plugins.scripted_process import ScriptedFrame
class DummyStopHook:
@@ -22,7 +23,7 @@ class DummyScriptedProcess(ScriptedProcess):
def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData):
super().__init__(exe_ctx, args)
- self.threads[0] = DummyScriptedThread(self, None)
+ self.threads[0] = DummyScriptedThread(self, args)
self.memory = {}
addr = 0x500000000
debugger = self.target.GetDebugger()
@@ -69,6 +70,9 @@ class DummyScriptedThread(ScriptedThread):
def __init__(self, process, args):
super().__init__(process, args)
self.frames.append({"pc": 0x0100001B00})
+ self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "baz123"))
+ self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "bar"))
+ self.frames.append(DummyScriptedFrame(self, args, len(self.frames), "foo"))
def get_thread_id(self) -> int:
return 0x19
@@ -109,6 +113,65 @@ class DummyScriptedThread(ScriptedThread):
)
+class DummyScriptedFrame(ScriptedFrame):
+ def __init__(self, thread, args, id, name, sym_ctx=None):
+ super().__init__(thread, args)
+ self.id = id
+ self.name = name
+ self.sym_ctx = sym_ctx
+
+ def get_id(self):
+ return self.id
+
+ def get_function_name(self):
+ return self.name
+
+ def get_register_context(self) -> str:
+ return struct.pack(
+ "21Q",
+ 0x10001,
+ 0x10002,
+ 0x10003,
+ 0x10004,
+ 0x10005,
+ 0x10006,
+ 0x10007,
+ 0x10008,
+ 0x10009,
+ 0x100010,
+ 0x100011,
+ 0x100012,
+ 0x100013,
+ 0x100014,
+ 0x100015,
+ 0x100016,
+ 0x100017,
+ 0x100018,
+ 0x100019,
+ 0x100020,
+ 0x100021,
+ )
+
+ def get_symbol_context(self):
+ def get_symbol_context_for_function(func_name):
+ module = self.target.FindModule(self.target.GetExecutable())
+ if not module.IsValid():
+ return None
+
+ sym_ctx_list = module.FindFunctions(func_name)
+ if not sym_ctx_list.IsValid() or sym_ctx_list.GetSize() == 0:
+ return None
+
+ return sym_ctx_list.GetContextAtIndex(0)
+
+ return (
+ self.sym_ctx if self.sym_ctx else get_symbol_context_for_function(self.name)
+ )
+
+ def get_scripted_frame_plugin(self):
+ return DummyScriptedFrame.__module__ + "." + DummyScriptedFrame.__name__
+
+
def __lldb_init_module(debugger, dict):
# This is used when loading the script in an interactive debug session to
# automatically, register the stop-hook and launch the scripted process.
diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py
index 33cd79736dc3..ca376cc595f3 100644
--- a/lldb/test/API/functionalities/statusline/TestStatusline.py
+++ b/lldb/test/API/functionalities/statusline/TestStatusline.py
@@ -124,6 +124,7 @@ class TestStatusline(PExpectTest):
@skipIfRemote
@skipIfWindows
@skipIfDarwin
+ @skipIfLinux # https://github.com/llvm/llvm-project/issues/154763
@add_test_categories(["lldb-server"])
def test_modulelist_deadlock(self):
"""Regression test for a deadlock that occurs when the status line is enabled before connecting
diff --git a/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py b/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py
index 965da02ed0f9..afdc92dd4000 100644
--- a/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py
+++ b/lldb/test/API/functionalities/thread/step_until/TestStepUntil.py
@@ -1,10 +1,9 @@
"""Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms."""
-
-import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test_event.build_exception import BuildError
class StepUntilTestCase(TestBase):
@@ -112,10 +111,15 @@ class StepUntilTestCase(TestBase):
@no_debug_info_test
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
@skipIf(archs=no_match(["x86_64", "aarch64"]))
+ @skipIf(compiler=no_match(["clang"]))
def test_bad_line_discontinuous(self):
"""Test that we get an error if attempting to step outside the current
function -- and the function is discontinuous"""
- self.build(dictionary=self._build_dict_for_discontinuity())
+ try:
+ self.build(dictionary=self._build_dict_for_discontinuity())
+ except BuildError as ex:
+ self.skipTest(f"failed to build with linker script.")
+
_, _, thread, _ = lldbutil.run_to_source_breakpoint(
self, "At the start", lldb.SBFileSpec(self.main_source)
)
diff --git a/lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py b/lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py
index 59e028acf014..08d78fb996c7 100644
--- a/lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py
+++ b/lldb/test/API/functionalities/thread/step_until/TestStepUntilAPI.py
@@ -1,7 +1,7 @@
-import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test_event.build_exception import BuildError
class TestStepUntilAPI(TestBase):
@@ -74,15 +74,20 @@ class TestStepUntilAPI(TestBase):
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
@skipIf(archs=no_match(["x86_64", "aarch64"]))
+ @skipIf(compiler=no_match(["clang"]))
def test_hitting_discontinuous(self):
"""Test SBThread.StepOverUntil - targeting a line and hitting it -- with
discontinuous functions"""
- self._do_until(
- self._build_dict_for_discontinuity(),
- None,
- self.less_than_two,
- self.less_than_two,
- )
+ try:
+ self._do_until(
+ self._build_dict_for_discontinuity(),
+ None,
+ self.less_than_two,
+ self.less_than_two,
+ )
+ except BuildError as ex:
+ self.skipTest(f"failed to build with linker script.")
+
self._assertDiscontinuity()
def test_missing(self):
@@ -93,15 +98,20 @@ class TestStepUntilAPI(TestBase):
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
@skipIf(archs=no_match(["x86_64", "aarch64"]))
+ @skipIf(compiler=no_match(["clang"]))
def test_missing_discontinuous(self):
"""Test SBThread.StepOverUntil - targeting a line and missing it by
stepping out to call site -- with discontinuous functions"""
- self._do_until(
- self._build_dict_for_discontinuity(),
- ["foo", "bar", "baz"],
- self.less_than_two,
- self.back_out_in_main,
- )
+ try:
+ self._do_until(
+ self._build_dict_for_discontinuity(),
+ ["foo", "bar", "baz"],
+ self.less_than_two,
+ self.back_out_in_main,
+ )
+ except BuildError as ex:
+ self.skipTest(f"failed to build with linker script.")
+
self._assertDiscontinuity()
def test_bad_line(self):
@@ -120,13 +130,19 @@ class TestStepUntilAPI(TestBase):
@skipIf(oslist=lldbplatformutil.getDarwinOSTriples() + ["windows"])
@skipIf(archs=no_match(["x86_64", "aarch64"]))
+ @skipIf(compiler=no_match(["clang"]))
def test_bad_line_discontinuous(self):
"""Test that we get an error if attempting to step outside the current
function -- and the function is discontinuous"""
- self.build(dictionary=self._build_dict_for_discontinuity())
- _, _, thread, _ = lldbutil.run_to_source_breakpoint(
- self, "At the start", self.main_spec
- )
+
+ try:
+ self.build(dictionary=self._build_dict_for_discontinuity())
+ _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "At the start", self.main_spec
+ )
+ except BuildError as ex:
+ self.skipTest(f"failed to build with linker script.")
+
self.assertIn(
"step until target not in current function",
thread.StepOverUntil(
diff --git a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
index ca8b74e35dff..51a28c501307 100644
--- a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
+++ b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
@@ -63,11 +63,14 @@ class TsanBasicTestCase(TestBase):
substrs=["1 match found"],
)
- # We should be stopped in __tsan_on_report
process = self.dbg.GetSelectedTarget().process
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
- self.assertIn("__tsan_on_report", frame.GetFunctionName())
+ if self.platformIsDarwin():
+ # We should not be stopped in the sanitizer library.
+ self.assertIn("f2", frame.GetFunctionName())
+ else:
+ self.assertIn("__tsan_on_report", frame.GetFunctionName())
# The stopped thread backtrace should contain either line1 or line2
# from main.c.
diff --git a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
index 868a2864d2b5..9e9ea2114196 100644
--- a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
+++ b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
@@ -52,8 +52,11 @@ class UbsanBasicTestCase(TestBase):
substrs=["1 match found"],
)
- # We should be stopped in __ubsan_on_report
- self.assertIn("__ubsan_on_report", frame.GetFunctionName())
+ if self.platformIsDarwin():
+ # We should not be stopped in the sanitizer library.
+ self.assertIn("main", frame.GetFunctionName())
+ else:
+ self.assertIn("__ubsan_on_report", frame.GetFunctionName())
# The stopped thread backtrace should contain either 'align line'
found = False
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile b/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile
new file mode 100644
index 000000000000..22f1051530f8
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/Makefile
@@ -0,0 +1 @@
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py
new file mode 100644
index 000000000000..30b2a525eaab
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py
@@ -0,0 +1,73 @@
+"""
+Test that we can backtrace up an ARM Cortex-M Exception return stack
+"""
+
+import lldb
+import json
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCortexMExceptionUnwind(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ # on the lldb-remote-linux-ubuntu CI, the binary.json's triple of
+ # armv7m-apple is not being set in the Target triple, and we're
+ # picking the wrong ABI plugin, ABISysV_arm.
+ # ABISysV_arm::CreateDefaultUnwindPlan() doesn't have a way to detect
+ # arm/thumb for a stack frame, or even the Target's triple for a
+ # Cortex-M part that is always thumb. It hardcodes r11 as the frame
+ # pointer register, which is correct for arm code but not thumb.
+ # It is never correct # on a Cortex-M target.
+ # The Darwin ABIMacOSX_arm diverges from AAPCS and always uses r7 for
+ # the frame pointer -- the thumb convention -- whether executing arm or
+ # thumb. So its CreateDefaultUnwindPlan picks the correct register for
+ # the frame pointer, and we can walk the stack.
+ # ABISysV_arm::CreateDefaultUnwindPlan will only get one frame and
+ # not be able to continue.
+ #
+ # This may only be occuring on a 32-bit Ubuntu bot; need to test
+ # 64-bit Ubuntu and confirm.
+ @skipUnlessDarwin
+ def test_no_fpu(self):
+ """Test that we can backtrace correctly through an ARM Cortex-M Exception return stack"""
+
+ target = self.dbg.CreateTarget("")
+ exe = "binary.json"
+ with open(exe) as f:
+ exe_json = json.load(f)
+ exe_uuid = exe_json["uuid"]
+
+ target.AddModule(exe, "", exe_uuid)
+ self.assertTrue(target.IsValid())
+
+ core = self.getBuildArtifact("core")
+ self.yaml2macho_core("armv7m-nofpu-exception.yaml", core, exe_uuid)
+
+ process = target.LoadCore(core)
+ self.assertTrue(process.IsValid())
+
+ if self.TraceOn():
+ self.runCmd("target list")
+ self.runCmd("image list")
+ self.runCmd("target modules dump sections")
+ self.runCmd("target modules dump symtab")
+ self.runCmd("bt")
+
+ thread = process.GetThreadAtIndex(0)
+ self.assertTrue(thread.IsValid())
+
+ # We have 4 named stack frames and two unnamed
+ # frames above that. The topmost two stack frames
+ # were not interesting for this test, so I didn't
+ # create symbols for them.
+ self.assertEqual(thread.GetNumFrames(), 6)
+ stackframe_names = [
+ "exception_catcher",
+ "exception_catcher",
+ "exception_thrower",
+ "main",
+ ]
+ for i, name in enumerate(stackframe_names):
+ self.assertEqual(name, thread.GetFrameAtIndex(i).GetSymbol().GetName())
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml
new file mode 100644
index 000000000000..9ce5ff49d9b6
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml
@@ -0,0 +1,64 @@
+cpu: armv7m
+threads:
+ - regsets:
+ - flavor: gpr
+ registers: [{name: sp, value: 0x2000fe70}, {name: r7, value: 0x2000fe80},
+ {name: pc, value: 0x0020392c}, {name: lr, value: 0x0020392d}]
+memory-regions:
+ # stack memory fetched via
+ # (lldb) p/x $sp
+ # (lldb) x/128wx $sp
+ # % pbpaste | sed -e 's,.*: ,,' -e 's/ /, /g' -e 's/$/,/'
+ - addr: 0x2000fe70
+ UInt32: [
+ 0x0000002a, 0x20010e58, 0x00203923, 0x00000001,
+ 0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9,
+ 0x00000102, 0x00000002, 0x000003f0, 0x0000002a,
+ 0x20012620, 0x00203215, 0x00203366, 0x81000200,
+ 0x00203215, 0x200128b0, 0x0024928d, 0x2000fecc,
+ 0x002491ed, 0x20010e58, 0x20010e4c, 0x2000ffa0,
+ 0x200107a0, 0x0000003c, 0x200116e8, 0x200108b0,
+ 0x0020b895, 0x00000000, 0x0000e200, 0x2001227d,
+ 0x200121fd, 0x0000e000, 0x00000000, 0x200129a0,
+ 0x002035bf, 0x00000029, 0x000003d8, 0x20011120,
+ 0x200116e0, 0x40003800, 0x20011120, 0x00000000,
+ 0x00205169, 0x00203713, 0x00000000, 0x0022dcb9,
+ 0x40003800, 0x20011240, 0x00000000, 0xf7d71ecf,
+ 0xfc7676d6, 0x00000000, 0x968782d3, 0xe75afbbb,
+ 0x600d77c8, 0xc1c05886, 0x17f3e76d, 0xefc3054d,
+ 0x11940aaa, 0x00000000, 0x93bffabb, 0x6db85af0,
+ 0x00000000, 0x2001d76f, 0xcb35f653, 0x00000000,
+ 0x00000000, 0x079d5058, 0x00000000, 0x00000000,
+ 0xc5622949, 0x68682572, 0x00000075, 0x0000e500,
+ 0x20012c30, 0x00000000, 0xcdfcd8c2, 0x76efc90f,
+ 0x0024495f, 0x20012bf0, 0x0000e400, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0029089c, 0x0029089c, 0x00000000, 0x2000ffe4,
+ 0x00202a87, 0x2000ffec, 0x00200257, 0x2000fff4,
+ 0x00200211, 0x00000000, 0x00000000, 0x7badb3f6,
+ 0x20010794, 0x20010fac, 0x200109b0, 0x002887a4,
+ 0x00285688, 0x002854c8, 0x00288f74, 0x0028a618,
+ 0x0028a6f8, 0x00000000, 0x00000001, 0x00000000,
+ 0x00000000, 0x00000000, 0x002037dd, 0x00000000,
+ 0x00000002, 0x00000100, 0x00000000, 0x20010064,
+ 0x00000000, 0x00000000, 0x00000000, 0x200109c0,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+ ]
+ # exception_catcher() function bytes
+ # (lldb) dis
+ # binary`exception_catcher:
+ # 0x203910 <+0>: push {r3, r4, r5, r6, r7, lr}
+ # 0x203912 <+2>: add r7, sp, #0x10
+ # ...
+ # (lldb) x/44bx 0x203910
+ # % pbpaste | sed -e 's,.*: ,,' -e 's/ /, /g' -e 's/$/,/'
+ - addr: 0x203910
+ UInt8: [
+ 0xf8, 0xb5, 0x04, 0xaf, 0x06, 0x4c, 0x07, 0x49,
+ 0x74, 0xf0, 0x2e, 0xf8, 0x01, 0xac, 0x74, 0xf0,
+ 0x61, 0xf8, 0x05, 0x48, 0x76, 0xf0, 0xdf, 0xfe,
+ 0x74, 0xf0, 0x0b, 0xf9, 0xfe, 0xe7, 0x00, 0xbf,
+ 0x4c, 0x0e, 0x01, 0x20, 0x0d, 0x35, 0x20, 0x00,
+ 0x98, 0xae, 0x28, 0x00
+ ]
+
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json
new file mode 100644
index 000000000000..8fcd5307ff82
--- /dev/null
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json
@@ -0,0 +1,41 @@
+{
+ "triple": "armv7m-apple",
+ "uuid": "2D157DBA-53C9-3AC7-B5A1-9D336EC831CB",
+ "type": "executable",
+ "sections": [
+ {
+ "user_id": 100,
+ "name": "TEXT",
+ "type": "code",
+ "address": 2097664,
+ "size": 598872,
+ "file_offset": 0,
+ "file_size": 598872,
+ "alignment": 2,
+ "flags": 514,
+ "read": true,
+ "write": false,
+ "execute": true
+ }
+ ],
+ "symbols": [
+ {
+ "name": "main",
+ "type": "code",
+ "size": 10,
+ "address": 2108030
+ },
+ {
+ "name": "exception_catcher",
+ "type": "code",
+ "size": 44,
+ "address": 2111760
+ },
+ {
+ "name": "exception_thrower",
+ "type": "code",
+ "size": 2652,
+ "address": 2108040
+ }
+ ]
+}
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/Makefile b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
new file mode 100644
index 000000000000..87d8adb42b82
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
@@ -0,0 +1,116 @@
+"""
+Test that we can call structors/destructors
+annotated (and thus mangled) with ABI tags.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AbiTagStructorsTestCase(TestBase):
+ @expectedFailureAll(oslist=["windows"])
+ def test_with_structor_linkage_names(self):
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
+
+ lldbutil.run_to_source_breakpoint(
+ self, "Break here", lldb.SBFileSpec("main.cpp", False)
+ )
+
+ self.expect_expr(
+ "Tagged()",
+ result_type="Tagged",
+ result_children=[ValueCheck(name="x", value="15")],
+ )
+ self.expect_expr(
+ "Tagged(-17)",
+ result_type="Tagged",
+ result_children=[ValueCheck(name="x", value="-17")],
+ )
+ self.expect_expr("t1 = t2", result_type="Tagged")
+
+ self.expect("expr Tagged t3(t1)", error=False)
+ self.expect("expr t1.~Tagged()", error=False)
+
+ self.expect("expr t1.~Tagged()", error=False)
+
+ self.expect(
+ "expression -- struct $Derived : virtual public Tagged { int y; $Derived(int val) : Tagged(val) { y = x; } };",
+ error=False,
+ )
+ self.expect(
+ "expression -- struct $Derived2 : virtual public $Derived { int z; $Derived2() : $Derived(10) { z = y; } };",
+ error=False,
+ )
+ self.expect_expr(
+ "$Derived2 d; d",
+ result_type="$Derived2",
+ result_children=[
+ ValueCheck(
+ name="$Derived",
+ children=[
+ ValueCheck(
+ name="Tagged", children=[ValueCheck(name="x", value="15")]
+ ),
+ ValueCheck(name="y", value="15"),
+ ],
+ ),
+ ValueCheck(name="z", value="15"),
+ ],
+ )
+
+ # Calls to deleting and base object destructor variants (D0 and D2 in Itanium ABI)
+ self.expect_expr(
+ "struct D : public HasVirtualDtor {}; D d; d.func()",
+ result_type="int",
+ result_value="10",
+ )
+
+ @expectedFailureAll(oslist=["windows"])
+ def test_no_structor_linkage_names(self):
+ """
+ Test that without linkage names on structor declarations we can't call
+ ABI-tagged structors.
+ """
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+
+ lldbutil.run_to_source_breakpoint(
+ self, "Break here", lldb.SBFileSpec("main.cpp", False)
+ )
+
+ self.expect("expression Tagged(17)", error=True)
+ self.expect("expr Tagged t3(t1)", error=True)
+ self.expect("expr t1.~Tagged()", error=True)
+
+ ## Calls to deleting and base object destructor variants (D0 and D2 in Itanium ABI)
+ self.expect(
+ "expression -- struct D : public HasVirtualDtor {}; D d; d.func()",
+ error=True,
+ )
+
+ self.expect("expression -- Derived d(16); d", error=True)
+
+ def do_nested_structor_test(self):
+ """
+ Test that calling ABI-tagged ctors of function local classes is not supported,
+ but calling un-tagged functions is.
+ """
+ lldbutil.run_to_source_breakpoint(
+ self, "Break nested", lldb.SBFileSpec("main.cpp", False)
+ )
+
+ self.expect("expression Local()", error=False)
+ self.expect(
+ "expression TaggedLocal()", error=True, substrs=["Couldn't look up symbols"]
+ )
+
+ @expectedFailureAll(oslist=["windows"])
+ def test_nested_no_structor_linkage_names(self):
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
+ self.do_nested_structor_test()
+
+ @expectedFailureAll(oslist=["windows"])
+ def test_nested_with_structor_linkage_names(self):
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+ self.do_nested_structor_test()
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
new file mode 100644
index 000000000000..ddf237491b83
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
@@ -0,0 +1,62 @@
+#include <cstdio>
+
+struct Tagged {
+ [[gnu::abi_tag("Default")]] Tagged() : x(15) { std::puts(__func__); }
+ [[gnu::abi_tag("Value")]] Tagged(int val) : x(val) { std::puts(__func__); }
+ [[gnu::abi_tag("Copy")]] Tagged(const Tagged &lhs) : x(lhs.x) {
+ std::puts(__func__);
+ }
+ [[gnu::abi_tag("CopyAssign")]] Tagged &operator=(const Tagged &) {
+ std::puts(__func__);
+ return *this;
+ }
+ [[gnu::abi_tag("Dtor")]] ~Tagged() { std::puts(__func__); }
+
+ int x;
+};
+
+struct Base {
+ virtual ~Base() { std::puts(__func__); }
+ virtual int func() { return 5; }
+};
+
+struct HasVirtualDtor : public Base {
+ int func() override { return 10; }
+
+ [[gnu::abi_tag("VirtualDtor")]] ~HasVirtualDtor() override {
+ std::puts(__func__);
+ }
+};
+
+struct HasNestedCtor {
+ HasNestedCtor() {
+ struct TaggedLocal {
+ [[gnu::abi_tag("Local")]] TaggedLocal() { std::puts(__func__); }
+ };
+
+ struct Local {
+ Local() { std::puts(__func__); }
+ };
+
+ TaggedLocal l1;
+ Local l2;
+ std::puts("Break nested");
+ }
+};
+
+int main() {
+ Tagged t;
+ Tagged t1(10);
+ Tagged t2(t1);
+ t1 = t2;
+
+ Base b;
+ HasVirtualDtor vdtor;
+ vdtor.func();
+
+ std::puts("Break here");
+
+ HasNestedCtor nested;
+
+ return 0;
+}
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
index 70d7fd096c7c..c0545c70c84e 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
@@ -5,15 +5,14 @@ from lldbsuite.test import lldbutil
class ExprDefinitionInDylibTestCase(TestBase):
- NO_DEBUG_INFO_TESTCASE = True
@skipIfWindows
- def test(self):
+ def test_with_structor_linkage_names(self):
"""
Tests that we can call functions whose definition
is in a different LLDB module than it's declaration.
"""
- self.build()
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.assertTrue(target, VALID_TARGET)
@@ -31,10 +30,71 @@ class ExprDefinitionInDylibTestCase(TestBase):
)
self.expect_expr("f.method()", result_value="-72", result_type="int")
- self.expect_expr("Foo()", result_type="Foo")
- # FIXME: mangled name lookup for ABI-tagged ctors fails because
- # the debug-info AST doesn't have ABI-tag information.
- self.expect(
- "expr Bar()", error=True, substrs=["error: Couldn't look up symbols"]
+ self.expect_expr("Foo(10)", result_type="Foo")
+
+ self.expect_expr("Base()", result_type="Base")
+
+ self.expect_expr("Bar()", result_type="Bar")
+
+ # Test a more complex setup: expression that has a three bases:
+ # 1. definition is in local module
+ # 2. definition is in different module
+ # 3. definition is in expression context (and has it's own virtual base)
+ self.expect_expr(
+ "struct ExprBase : virtual Foo { int z; ExprBase() : Foo(11) { z = x; } }; struct Expr : virtual Local, virtual Foo, virtual ExprBase { int w; Expr() : Local(), Foo(12), ExprBase() { w = y; } }; Expr tmp; tmp",
+ result_type="Expr",
+ result_children=[
+ ValueCheck(
+ name="Local",
+ children=[
+ ValueCheck(
+ name="Foo", children=[ValueCheck(name="x", value="12")]
+ ),
+ ValueCheck(name="y", value="12"),
+ ],
+ ),
+ ValueCheck(name="Foo", children=[ValueCheck(name="x", value="12")]),
+ ValueCheck(
+ name="ExprBase",
+ children=[
+ ValueCheck(
+ name="Foo", children=[ValueCheck(name="x", value="12")]
+ ),
+ ValueCheck(name="z", value="12"),
+ ],
+ ),
+ ValueCheck(name="w", value="12"),
+ ],
)
+
+ @skipIfWindows
+ def test_no_structor_linkage_names(self):
+ """
+ Tests that if structor declarations don't have linkage names, we can't
+ call ABI-tagged constructors. But non-tagged ones are fine.
+ """
+ self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+
+ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+ self.assertTrue(target, VALID_TARGET)
+
+ env = self.registerSharedLibrariesWithTarget(target, ["lib"])
+
+ breakpoint = lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", line_number("main.cpp", "return")
+ )
+
+ process = target.LaunchSimple(None, env, self.get_process_working_directory())
+
+ self.assertIsNotNone(
+ lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(), breakpoint)
+ )
+
+ self.expect_expr("f.method()", result_value="-72", result_type="int")
+
+ self.expect_expr("Foo(10)", result_type="Foo")
+
+ self.expect("Base()", error=True)
+
+ self.expect("Bar()", error=True)
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
index 1a08817f5cda..aa3921ffe0b1 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
@@ -4,10 +4,14 @@
int Foo::method() { return -72; }
-Foo::Foo() { std::puts(__func__); }
+Foo::Foo(int val) : x(val) { std::puts(__func__); }
Foo::~Foo() { std::puts(__func__); }
Bar::Bar() { std::puts(__func__); }
Bar::~Bar() { std::puts(__func__); }
+
+Base::Base() { std::puts(__func__); }
+
+Base::~Base() { std::puts(__func__); }
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
index 5ec227946cba..513c9a0f9c87 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
@@ -3,11 +3,18 @@
struct Foo {
int method();
- Foo();
+ Foo(int val);
~Foo();
+
+ int x;
+};
+
+struct Base {
+ [[gnu::abi_tag("BaseCtor")]] Base();
+ [[gnu::abi_tag("BaseDtor")]] ~Base();
};
-struct Bar {
+struct Bar : public Base {
[[gnu::abi_tag("Ctor")]] Bar();
[[gnu::abi_tag("Dtor")]] ~Bar();
};
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
index 4d6bece21eca..ff43190b991c 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
@@ -1,7 +1,18 @@
#include "lib.h"
+struct Local : public virtual Foo {
+ Local();
+ ~Local();
+ int y;
+};
+
+Local::Local() : Foo(5) { y = x; }
+Local::~Local() {}
+
int main() {
- Foo f;
- Bar b;
+ Foo f(5);
+ Base b1;
+ Bar b2;
+ Local l1;
return f.method();
}
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
index 8efa53bdbf72..d8a729b322fe 100644
--- a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
@@ -9,7 +9,7 @@ class LibCxxInternalsRecognizerTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@add_test_categories(["libc++"])
- @skipIf(compiler="clang", compiler_version=["<", "16.0"])
+ @skipIf(compiler="clang", compiler_version=["<", "19.0"])
def test_frame_recognizer(self):
"""Test that implementation details of libc++ are hidden"""
self.build()
@@ -40,6 +40,7 @@ class LibCxxInternalsRecognizerTestCase(TestBase):
"Callable::operator()(int) const": ["::invoke", "test_invoke"],
# Containers
"MyKey::operator<(MyKey const&) const": [
+ "::operator()",
"less",
"::emplace",
"test_containers",
diff --git a/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
index ec8eb1c05dfb..2fa963efcc8f 100644
--- a/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
+++ b/lldb/test/API/linux/aarch64/tls_registers/TestAArch64LinuxTLSRegisters.py
@@ -53,6 +53,8 @@ class AArch64LinuxTLSRegisters(TestBase):
tls_reg.IsValid(), "{} register not found.".format(register)
)
self.assertEqual(tls_reg.GetValueAsUnsigned(), values[register])
+ if register == "tpidr":
+ self.expect("reg read tp", substrs=[hex(values[register])])
def check_tls_reg(self, registers):
self.setup(registers)
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/Makefile b/lldb/test/API/macosx/arm-corefile-regctx/Makefile
deleted file mode 100644
index e1d0354441cd..000000000000
--- a/lldb/test/API/macosx/arm-corefile-regctx/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-MAKE_DSYM := NO
-
-CXX_SOURCES := create-arm-corefiles.cpp
-
-include Makefile.rules
-
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py b/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
index 6754288a65e1..a2890cdfeaa4 100644
--- a/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
+++ b/lldb/test/API/macosx/arm-corefile-regctx/TestArmMachoCorefileRegctx.py
@@ -13,20 +13,14 @@ from lldbsuite.test import lldbutil
class TestArmMachoCorefileRegctx(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- @skipUnlessDarwin
- def setUp(self):
- TestBase.setUp(self)
- self.build()
- self.create_corefile = self.getBuildArtifact("a.out")
- self.corefile = self.getBuildArtifact("core")
-
def test_armv7_corefile(self):
### Create corefile
- retcode = call(self.create_corefile + " armv7 " + self.corefile, shell=True)
+ corefile = self.getBuildArtifact("core")
+ self.yaml2macho_core("armv7m.yaml", corefile)
target = self.dbg.CreateTarget("")
err = lldb.SBError()
- process = target.LoadCore(self.corefile)
+ process = target.LoadCore(corefile)
self.assertTrue(process.IsValid())
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
@@ -50,11 +44,12 @@ class TestArmMachoCorefileRegctx(TestBase):
def test_arm64_corefile(self):
### Create corefile
- retcode = call(self.create_corefile + " arm64 " + self.corefile, shell=True)
+ corefile = self.getBuildArtifact("core")
+ self.yaml2macho_core("arm64.yaml", corefile)
target = self.dbg.CreateTarget("")
err = lldb.SBError()
- process = target.LoadCore(self.corefile)
+ process = target.LoadCore(corefile)
self.assertTrue(process.IsValid())
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml b/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml
new file mode 100644
index 000000000000..fe06f2d4054b
--- /dev/null
+++ b/lldb/test/API/macosx/arm-corefile-regctx/arm64.yaml
@@ -0,0 +1,30 @@
+cpu: arm64
+threads:
+ # (lldb) reg read
+ # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: " $2 "},"}'
+ - regsets:
+ - flavor: gpr
+ registers: [
+ {name: x0, value: 0x0000000000000001}, {name: x1, value: 0x000000016fdff3c0},
+ {name: x2, value: 0x000000016fdff3d0}, {name: x3, value: 0x000000016fdff510},
+ {name: x4, value: 0x0000000000000000}, {name: x5, value: 0x0000000000000000},
+ {name: x6, value: 0x0000000000000000}, {name: x7, value: 0x0000000000000000},
+ {name: x8, value: 0x000000010000d910}, {name: x9, value: 0x0000000000000001},
+ {name: x10, value: 0xe1e88de000000000}, {name: x11, value: 0x0000000000000003},
+ {name: x12, value: 0x0000000000000148}, {name: x13, value: 0x0000000000004000},
+ {name: x14, value: 0x0000000000000008}, {name: x15, value: 0x0000000000000000},
+ {name: x16, value: 0x0000000000000000}, {name: x17, value: 0x0000000100003f5c},
+ {name: x18, value: 0x0000000000000000}, {name: x19, value: 0x0000000100003f5c},
+ {name: x20, value: 0x000000010000c000}, {name: x21, value: 0x000000010000d910},
+ {name: x22, value: 0x000000016fdff250}, {name: x23, value: 0x000000018ce12366},
+ {name: x24, value: 0x000000016fdff1d0}, {name: x25, value: 0x0000000000000001},
+ {name: x26, value: 0x0000000000000000}, {name: x27, value: 0x0000000000000000},
+ {name: x28, value: 0x0000000000000000}, {name: fp, value: 0x000000016fdff3a0},
+ {name: lr, value: 0x000000018cd97f28}, {name: sp, value: 0x000000016fdff140},
+ {name: pc, value: 0x0000000100003f5c}, {name: cpsr, value: 0x80001000}
+ ]
+ - flavor: exc
+ registers: [ {name: far, value: 0x0000000100003f5c},
+ {name: esr, value: 0xf2000000},
+ {name: exception, value: 0x00000000}
+ ]
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml b/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml
new file mode 100644
index 000000000000..411c12a7e407
--- /dev/null
+++ b/lldb/test/API/macosx/arm-corefile-regctx/armv7m.yaml
@@ -0,0 +1,36 @@
+cpu: armv7m
+threads:
+ # (lldb) reg read
+ # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: " $2 "},"}'
+ - regsets:
+ - flavor: gpr
+ registers: [
+ {name: r0, value: 0x00010000}, {name: r1, value: 0x00020000},
+ {name: r2, value: 0x00030000}, {name: r3, value: 0x00040000},
+ {name: r4, value: 0x00050000}, {name: r5, value: 0x00060000},
+ {name: r6, value: 0x00070000}, {name: r7, value: 0x00080000},
+ {name: r8, value: 0x00090000}, {name: r9, value: 0x000a0000},
+ {name: r10, value: 0x000b0000}, {name: r11, value: 0x000c0000},
+ {name: r12, value: 0x000d0000}, {name: sp, value: 0x000e0000},
+ {name: lr, value: 0x000f0000}, {name: pc, value: 0x00100000},
+ {name: cpsr, value: 0x00110000}
+ ]
+ - flavor: exc
+ registers: [ {name: far, value: 0x00003f5c},
+ {name: esr, value: 0xf2000000},
+ {name: exception, value: 0x00000000}
+ ]
+
+memory-regions:
+ # $sp is 0x000e0000, have bytes surrounding that address
+ - addr: 0x000dffe0
+ UInt8: [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
+ 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
+ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
+ 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+ 0x3f
+ ]
diff --git a/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp b/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp
deleted file mode 100644
index db39f12ecfb7..000000000000
--- a/lldb/test/API/macosx/arm-corefile-regctx/create-arm-corefiles.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-#include <mach-o/loader.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string>
-#include <vector>
-
-
-// Normally these are picked up by including <mach/thread_status.h>
-// but that does a compile time check for the build host arch and
-// only defines the ARM register context constants when building on
-// an arm system. We're creating fake corefiles, and might be
-// creating them on an intel system.
-#ifndef ARM_THREAD_STATE
-#define ARM_THREAD_STATE 1
-#endif
-#ifndef ARM_THREAD_STATE_COUNT
-#define ARM_THREAD_STATE_COUNT 17
-#endif
-#ifndef ARM_EXCEPTION_STATE
-#define ARM_EXCEPTION_STATE 3
-#endif
-#ifndef ARM_EXCEPTION_STATE_COUNT
-#define ARM_EXCEPTION_STATE_COUNT 3
-#endif
-#ifndef ARM_THREAD_STATE64
-#define ARM_THREAD_STATE64 6
-#endif
-#ifndef ARM_THREAD_STATE64_COUNT
-#define ARM_THREAD_STATE64_COUNT 68
-#endif
-#ifndef ARM_EXCEPTION_STATE64
-#define ARM_EXCEPTION_STATE64 7
-#endif
-#ifndef ARM_EXCEPTION_STATE64_COUNT
-#define ARM_EXCEPTION_STATE64_COUNT 4
-#endif
-
-union uint32_buf {
- uint8_t bytebuf[4];
- uint32_t val;
-};
-
-union uint64_buf {
- uint8_t bytebuf[8];
- uint64_t val;
-};
-
-void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
- uint64_buf conv;
- conv.val = val;
- for (int i = 0; i < 8; i++)
- buf.push_back(conv.bytebuf[i]);
-}
-
-void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
- uint32_buf conv;
- conv.val = val;
- for (int i = 0; i < 4; i++)
- buf.push_back(conv.bytebuf[i]);
-}
-
-std::vector<uint8_t> armv7_lc_thread_load_command() {
- std::vector<uint8_t> data;
- add_uint32(data, LC_THREAD); // thread_command.cmd
- add_uint32(data, 104); // thread_command.cmdsize
- add_uint32(data, ARM_THREAD_STATE); // thread_command.flavor
- add_uint32(data, ARM_THREAD_STATE_COUNT); // thread_command.count
- add_uint32(data, 0x00010000); // r0
- add_uint32(data, 0x00020000); // r1
- add_uint32(data, 0x00030000); // r2
- add_uint32(data, 0x00040000); // r3
- add_uint32(data, 0x00050000); // r4
- add_uint32(data, 0x00060000); // r5
- add_uint32(data, 0x00070000); // r6
- add_uint32(data, 0x00080000); // r7
- add_uint32(data, 0x00090000); // r8
- add_uint32(data, 0x000a0000); // r9
- add_uint32(data, 0x000b0000); // r10
- add_uint32(data, 0x000c0000); // r11
- add_uint32(data, 0x000d0000); // r12
- add_uint32(data, 0x000e0000); // sp
- add_uint32(data, 0x000f0000); // lr
- add_uint32(data, 0x00100000); // pc
- add_uint32(data, 0x00110000); // cpsr
-
- add_uint32(data, ARM_EXCEPTION_STATE); // thread_command.flavor
- add_uint32(data, ARM_EXCEPTION_STATE_COUNT); // thread_command.count
- add_uint32(data, 0x00003f5c); // far
- add_uint32(data, 0xf2000000); // esr
- add_uint32(data, 0x00000000); // exception
-
- return data;
-}
-
-std::vector<uint8_t> arm64_lc_thread_load_command() {
- std::vector<uint8_t> data;
- add_uint32(data, LC_THREAD); // thread_command.cmd
- add_uint32(data, 312); // thread_command.cmdsize
- add_uint32(data, ARM_THREAD_STATE64); // thread_command.flavor
- add_uint32(data, ARM_THREAD_STATE64_COUNT); // thread_command.count
- add_uint64(data, 0x0000000000000001); // x0
- add_uint64(data, 0x000000016fdff3c0); // x1
- add_uint64(data, 0x000000016fdff3d0); // x2
- add_uint64(data, 0x000000016fdff510); // x3
- add_uint64(data, 0x0000000000000000); // x4
- add_uint64(data, 0x0000000000000000); // x5
- add_uint64(data, 0x0000000000000000); // x6
- add_uint64(data, 0x0000000000000000); // x7
- add_uint64(data, 0x000000010000d910); // x8
- add_uint64(data, 0x0000000000000001); // x9
- add_uint64(data, 0xe1e88de000000000); // x10
- add_uint64(data, 0x0000000000000003); // x11
- add_uint64(data, 0x0000000000000148); // x12
- add_uint64(data, 0x0000000000004000); // x13
- add_uint64(data, 0x0000000000000008); // x14
- add_uint64(data, 0x0000000000000000); // x15
- add_uint64(data, 0x0000000000000000); // x16
- add_uint64(data, 0x0000000100003f5c); // x17
- add_uint64(data, 0x0000000000000000); // x18
- add_uint64(data, 0x0000000100003f5c); // x19
- add_uint64(data, 0x000000010000c000); // x20
- add_uint64(data, 0x000000010000d910); // x21
- add_uint64(data, 0x000000016fdff250); // x22
- add_uint64(data, 0x000000018ce12366); // x23
- add_uint64(data, 0x000000016fdff1d0); // x24
- add_uint64(data, 0x0000000000000001); // x25
- add_uint64(data, 0x0000000000000000); // x26
- add_uint64(data, 0x0000000000000000); // x27
- add_uint64(data, 0x0000000000000000); // x28
- add_uint64(data, 0x000000016fdff3a0); // fp
- add_uint64(data, 0x000000018cd97f28); // lr
- add_uint64(data, 0x000000016fdff140); // sp
- add_uint64(data, 0x0000000100003f5c); // pc
- add_uint32(data, 0x80001000); // cpsr
-
- add_uint32(data, 0x00000000); // padding
-
- add_uint32(data, ARM_EXCEPTION_STATE64); // thread_command.flavor
- add_uint32(data, ARM_EXCEPTION_STATE64_COUNT); // thread_command.count
- add_uint64(data, 0x0000000100003f5c); // far
- add_uint32(data, 0xf2000000); // esr
- add_uint32(data, 0x00000000); // exception
-
- return data;
-}
-
-std::vector<uint8_t> lc_segment(uint32_t fileoff,
- uint32_t lc_segment_data_size) {
- std::vector<uint8_t> data;
- // 0x000e0000 is the value of $sp in the armv7 LC_THREAD
- uint32_t start_vmaddr = 0x000e0000 - (lc_segment_data_size / 2);
- add_uint32(data, LC_SEGMENT); // segment_command.cmd
- add_uint32(data, sizeof(struct segment_command)); // segment_command.cmdsize
- for (int i = 0; i < 16; i++)
- data.push_back(0); // segment_command.segname[16]
- add_uint32(data, start_vmaddr); // segment_command.vmaddr
- add_uint32(data, lc_segment_data_size); // segment_command.vmsize
- add_uint32(data, fileoff); // segment_command.fileoff
- add_uint32(data, lc_segment_data_size); // segment_command.filesize
- add_uint32(data, 3); // segment_command.maxprot
- add_uint32(data, 3); // segment_command.initprot
- add_uint32(data, 0); // segment_command.nsects
- add_uint32(data, 0); // segment_command.flags
-
- return data;
-}
-
-enum arch { unspecified, armv7, arm64 };
-
-int main(int argc, char **argv) {
- if (argc != 3) {
- fprintf(stderr,
- "usage: create-arm-corefiles [armv7|arm64] <output-core-name>\n");
- exit(1);
- }
-
- arch arch = unspecified;
-
- if (strcmp(argv[1], "armv7") == 0)
- arch = armv7;
- else if (strcmp(argv[1], "arm64") == 0)
- arch = arm64;
- else {
- fprintf(stderr, "unrecognized architecture %s\n", argv[1]);
- exit(1);
- }
-
- // An array of load commands (in the form of byte arrays)
- std::vector<std::vector<uint8_t>> load_commands;
-
- // An array of corefile contents (page data, lc_note data, etc)
- std::vector<uint8_t> payload;
-
- // First add all the load commands / payload so we can figure out how large
- // the load commands will actually be.
- if (arch == armv7) {
- load_commands.push_back(armv7_lc_thread_load_command());
- load_commands.push_back(lc_segment(0, 0));
- } else if (arch == arm64) {
- load_commands.push_back(arm64_lc_thread_load_command());
- }
-
- int size_of_load_commands = 0;
- for (const auto &lc : load_commands)
- size_of_load_commands += lc.size();
-
- int header_and_load_cmd_room =
- sizeof(struct mach_header_64) + size_of_load_commands;
-
- // Erase the load commands / payload now that we know how much space is
- // needed, redo it.
- load_commands.clear();
- payload.clear();
-
- int payload_fileoff = (header_and_load_cmd_room + 4096 - 1) & ~(4096 - 1);
-
- const int lc_segment_data_size = 64;
- if (arch == armv7) {
- load_commands.push_back(armv7_lc_thread_load_command());
- load_commands.push_back(lc_segment(payload_fileoff, lc_segment_data_size));
- } else if (arch == arm64) {
- load_commands.push_back(arm64_lc_thread_load_command());
- }
-
- if (arch == armv7)
- for (int i = 0; i < lc_segment_data_size;
- i++) // from segment_command.filesize
- payload.push_back(i);
-
- struct mach_header_64 mh;
- int header_size;
- if (arch == armv7) {
- mh.magic = MH_MAGIC;
- mh.cputype = CPU_TYPE_ARM;
- mh.cpusubtype = CPU_SUBTYPE_ARM_V7M;
- header_size = sizeof(struct mach_header);
- } else if (arch == arm64) {
- mh.magic = MH_MAGIC_64;
- mh.cputype = CPU_TYPE_ARM64;
- mh.cpusubtype = CPU_SUBTYPE_ARM64_ALL;
- header_size = sizeof(struct mach_header_64);
- }
- mh.filetype = MH_CORE;
- mh.ncmds = load_commands.size();
- mh.sizeofcmds = size_of_load_commands;
- mh.flags = 0;
- mh.reserved = 0;
-
- FILE *f = fopen(argv[2], "w");
-
- if (f == nullptr) {
- fprintf(stderr, "Unable to open file %s for writing\n", argv[2]);
- exit(1);
- }
-
- fwrite(&mh, header_size, 1, f);
-
- for (const auto &lc : load_commands)
- fwrite(lc.data(), lc.size(), 1, f);
-
- fseek(f, payload_fileoff, SEEK_SET);
-
- fwrite(payload.data(), payload.size(), 1, f);
-
- fclose(f);
-}
diff --git a/lldb/test/API/macosx/arm-pointer-metadata-stripping/Makefile b/lldb/test/API/macosx/arm-pointer-metadata-stripping/Makefile
new file mode 100644
index 000000000000..c9319d6e6888
--- /dev/null
+++ b/lldb/test/API/macosx/arm-pointer-metadata-stripping/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/macosx/arm-pointer-metadata-stripping/TestArmPointerMetadataStripping.py b/lldb/test/API/macosx/arm-pointer-metadata-stripping/TestArmPointerMetadataStripping.py
new file mode 100644
index 000000000000..f61945b3eb4c
--- /dev/null
+++ b/lldb/test/API/macosx/arm-pointer-metadata-stripping/TestArmPointerMetadataStripping.py
@@ -0,0 +1,48 @@
+import lldb
+import json
+import os
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+@skipUnlessDarwin
+@skipIf(archs=no_match(["arm64", "arm64e"]))
+class TestArmPointerMetadataStripping(TestBase):
+ # Use extra_symbols.json as a template to add a new symbol whose address
+ # contains non-zero high order bits set.
+ def create_symbols_file(self):
+ template_path = os.path.join(self.getSourceDir(), "extra_symbols.json")
+ with open(template_path, "r") as f:
+ symbols_data = json.load(f)
+
+ target = self.dbg.GetSelectedTarget()
+ symbols_data["triple"] = target.GetTriple()
+
+ module = target.GetModuleAtIndex(0)
+ symbols_data["uuid"] = module.GetUUIDString()
+
+ json_filename = self.getBuildArtifact("extra_symbols.json")
+ with open(json_filename, "w") as file:
+ json.dump(symbols_data, file, indent=4)
+
+ return json_filename
+
+ def test(self):
+ self.build()
+ src = lldb.SBFileSpec("main.c")
+ target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+ self, "break here", src
+ )
+
+ symbols_file = self.create_symbols_file()
+ self.runCmd(f"target module add {symbols_file}")
+
+ # The high order bits should be stripped.
+ self.expect_expr("get_high_bits(&myglobal_json)", result_value="0")
+
+ # Mark all bits as used for addresses and ensure bits are no longer stripped.
+ self.runCmd("settings set target.process.virtual-addressable-bits 64")
+ self.expect_expr(
+ "get_high_bits(&myglobal_json)", result_value=str(0x1200000000000000)
+ )
diff --git a/lldb/test/API/macosx/arm-pointer-metadata-stripping/extra_symbols.json b/lldb/test/API/macosx/arm-pointer-metadata-stripping/extra_symbols.json
new file mode 100644
index 000000000000..5c2503d508b4
--- /dev/null
+++ b/lldb/test/API/macosx/arm-pointer-metadata-stripping/extra_symbols.json
@@ -0,0 +1,21 @@
+{
+ "triple": "replace me",
+ "uuid": "replace me",
+ "type": "executable",
+ "sections": [
+ {
+ "name": "__DATA",
+ "type": "data",
+ "address": 1297224342667202580,
+ "size": 16
+ }
+ ],
+ "symbols": [
+ {
+ "name": "myglobal_json",
+ "size": 8,
+ "type": "data",
+ "address": 1297224342667202580
+ }
+ ]
+}
diff --git a/lldb/test/API/macosx/arm-pointer-metadata-stripping/main.c b/lldb/test/API/macosx/arm-pointer-metadata-stripping/main.c
new file mode 100644
index 000000000000..05a85133caf7
--- /dev/null
+++ b/lldb/test/API/macosx/arm-pointer-metadata-stripping/main.c
@@ -0,0 +1,13 @@
+#include <stdint.h>
+
+uintptr_t get_high_bits(void *ptr) {
+ uintptr_t address_bits = 56;
+ uintptr_t mask = ~((1ULL << address_bits) - 1);
+ uintptr_t ptrtoint = (uintptr_t)ptr;
+ uintptr_t high_bits = ptrtoint & mask;
+ return high_bits;
+}
+
+int main() {
+ return 0; // break here
+}
diff --git a/lldb/test/API/macosx/riscv32-corefile/Makefile b/lldb/test/API/macosx/riscv32-corefile/Makefile
deleted file mode 100644
index 04f268758d00..000000000000
--- a/lldb/test/API/macosx/riscv32-corefile/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-MAKE_DSYM := NO
-CXX_SOURCES := create-empty-riscv-corefile.cpp
-EXE := create-empty-riscv-corefile
-
-all: create-empty-riscv-corefile
-
-include Makefile.rules
diff --git a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
index 8d11821d3898..449da70fb08c 100644
--- a/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
+++ b/lldb/test/API/macosx/riscv32-corefile/TestRV32MachOCorefile.py
@@ -13,18 +13,16 @@ from lldbsuite.test import lldbutil
class TestRV32MachOCorefile(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- @skipUnlessDarwin
+ @no_debug_info_test
def test_riscv32_gpr_corefile_registers(self):
- self.build()
- create_corefile = self.getBuildArtifact("create-empty-riscv-corefile")
corefile = self.getBuildArtifact("core")
- call(create_corefile + " " + corefile, shell=True)
+ self.yaml2macho_core("riscv32-registers.yaml", corefile)
target = self.dbg.CreateTarget("")
process = target.LoadCore(corefile)
process = target.GetProcess()
- self.assertEqual(process.GetNumThreads(), 1)
+ self.assertEqual(process.GetNumThreads(), 2)
thread = process.GetThreadAtIndex(0)
self.assertEqual(thread.GetNumFrames(), 1)
@@ -80,3 +78,12 @@ class TestRV32MachOCorefile(TestBase):
val = idx | (idx << 8) | (idx << 16) | (idx << 24)
self.assertEqual(gpr_regs.GetChildAtIndex(idx).GetValueAsUnsigned(), val)
idx = idx + 1
+
+ thread = process.GetThreadAtIndex(1)
+ self.assertEqual(thread.GetNumFrames(), 1)
+
+ frame = thread.GetFrameAtIndex(0)
+ gpr_regs = frame.registers.GetValueAtIndex(0)
+
+ self.assertEqual(gpr_regs.GetChildAtIndex(0).GetValueAsUnsigned(), 0x90000000)
+ self.assertEqual(gpr_regs.GetChildAtIndex(32).GetValueAsUnsigned(), 0x90202020)
diff --git a/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp b/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp
deleted file mode 100644
index 907cca3b70b4..000000000000
--- a/lldb/test/API/macosx/riscv32-corefile/create-empty-riscv-corefile.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include <inttypes.h>
-#include <mach-o/loader.h>
-#include <mach/thread_status.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-#include <sys/errno.h>
-#include <uuid/uuid.h>
-#include <vector>
-
-#define CPU_TYPE_RISCV 24
-#define CPU_SUBTYPE_RISCV_ALL 0
-#define RV32_THREAD_STATE 2
-// x0-x31 + pc, all 32-bit
-#define RV32_THREAD_STATE_COUNT 33
-
-union uint32_buf {
- uint8_t bytebuf[4];
- uint32_t val;
-};
-
-union uint64_buf {
- uint8_t bytebuf[8];
- uint64_t val;
-};
-
-void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
- uint64_buf conv;
- conv.val = val;
- for (int i = 0; i < 8; i++)
- buf.push_back(conv.bytebuf[i]);
-}
-
-void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
- uint32_buf conv;
- conv.val = val;
- for (int i = 0; i < 4; i++)
- buf.push_back(conv.bytebuf[i]);
-}
-
-std::vector<uint8_t> lc_thread_load_command() {
- std::vector<uint8_t> data;
- add_uint32(data, LC_THREAD); // thread_command.cmd
- add_uint32(data, 4 + 4 + 4 + 4 +
- (RV32_THREAD_STATE_COUNT * 4)); // thread_command.cmdsize
- add_uint32(data, RV32_THREAD_STATE); // thread_command.flavor
- add_uint32(data, RV32_THREAD_STATE_COUNT); // thread_command.count
- for (int i = 0; i < RV32_THREAD_STATE_COUNT; i++) {
- add_uint32(data, i | (i << 8) | (i << 16) | (i << 24));
- }
- return data;
-}
-
-int main(int argc, char **argv) {
- if (argc != 2) {
- fprintf(stderr,
- "usage: create-empty-riscv-corefile output-corefile-name\n");
- exit(1);
- }
-
- cpu_type_t cputype = CPU_TYPE_RISCV;
- cpu_subtype_t cpusubtype = CPU_SUBTYPE_RISCV_ALL;
-
- // An array of load commands (in the form of byte arrays)
- std::vector<std::vector<uint8_t>> load_commands;
-
- // An array of corefile contents (page data, lc_note data, etc)
- std::vector<uint8_t> payload;
-
- // First add all the load commands / payload so we can figure out how large
- // the load commands will actually be.
- load_commands.push_back(lc_thread_load_command());
-
- int size_of_load_commands = 0;
- for (const auto &lc : load_commands)
- size_of_load_commands += lc.size();
-
- int header_and_load_cmd_room =
- sizeof(struct mach_header_64) + size_of_load_commands;
-
- // Erase the load commands / payload now that we know how much space is
- // needed, redo it.
- load_commands.clear();
- payload.clear();
-
- load_commands.push_back(lc_thread_load_command());
-
- struct mach_header mh;
- mh.magic = MH_MAGIC;
- mh.cputype = cputype;
-
- mh.cpusubtype = cpusubtype;
- mh.filetype = MH_CORE;
- mh.ncmds = load_commands.size();
- mh.sizeofcmds = size_of_load_commands;
- mh.flags = 0;
-
- FILE *f = fopen(argv[1], "w");
-
- if (f == nullptr) {
- fprintf(stderr, "Unable to open file %s for writing\n", argv[1]);
- exit(1);
- }
-
- fwrite(&mh, sizeof(struct mach_header), 1, f);
-
- for (const auto &lc : load_commands)
- fwrite(lc.data(), lc.size(), 1, f);
-
- fseek(f, header_and_load_cmd_room, SEEK_SET);
-
- fwrite(payload.data(), payload.size(), 1, f);
-
- fclose(f);
-}
diff --git a/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml
new file mode 100644
index 000000000000..81c725f1a4f0
--- /dev/null
+++ b/lldb/test/API/macosx/riscv32-corefile/riscv32-registers.yaml
@@ -0,0 +1,46 @@
+cpu: riscv
+threads:
+ # (lldb) reg read
+ # % pbpaste | grep = | sed 's, ,,g' | awk -F= '{print "{name: " $1 ", value: " $2 "},"}'
+ - regsets:
+ - flavor: gpr
+ registers: [
+ {name: zero, value: 0x00000000}, {name: ra, value: 0x01010101},
+ {name: sp, value: 0x02020202}, {name: gp, value: 0x03030303},
+ {name: tp, value: 0x04040404}, {name: t0, value: 0x05050505},
+ {name: t1, value: 0x06060606}, {name: t2, value: 0x07070707},
+ {name: fp, value: 0x08080808}, {name: s1, value: 0x09090909},
+ {name: a0, value: 0x0a0a0a0a}, {name: a1, value: 0x0b0b0b0b},
+ {name: a2, value: 0x0c0c0c0c}, {name: a3, value: 0x0d0d0d0d},
+ {name: a4, value: 0x0e0e0e0e}, {name: a5, value: 0x0f0f0f0f},
+ {name: a6, value: 0x10101010}, {name: a7, value: 0x11111111},
+ {name: s2, value: 0x12121212}, {name: s3, value: 0x13131313},
+ {name: s4, value: 0x14141414}, {name: s5, value: 0x15151515},
+ {name: s6, value: 0x16161616}, {name: s7, value: 0x17171717},
+ {name: s8, value: 0x18181818}, {name: s9, value: 0x19191919},
+ {name: s10, value: 0x1a1a1a1a}, {name: s11, value: 0x1b1b1b1b},
+ {name: t3, value: 0x1c1c1c1c}, {name: t4, value: 0x1d1d1d1d},
+ {name: t5, value: 0x1e1e1e1e}, {name: t6, value: 0x1f1f1f1f},
+ {name: pc, value: 0x20202020}
+ ]
+ - regsets:
+ - flavor: gpr
+ registers: [
+ {name: zero, value: 0x90000000}, {name: ra, value: 0x91010101},
+ {name: sp, value: 0x92020202}, {name: gp, value: 0x93030303},
+ {name: tp, value: 0x94040404}, {name: t0, value: 0x95050505},
+ {name: t1, value: 0x96060606}, {name: t2, value: 0x97070707},
+ {name: fp, value: 0x98080808}, {name: s1, value: 0x99090909},
+ {name: a0, value: 0x9a0a0a0a}, {name: a1, value: 0x9b0b0b0b},
+ {name: a2, value: 0x9c0c0c0c}, {name: a3, value: 0x9d0d0d0d},
+ {name: a4, value: 0x9e0e0e0e}, {name: a5, value: 0x9f0f0f0f},
+ {name: a6, value: 0x90101010}, {name: a7, value: 0x91111111},
+ {name: s2, value: 0x92121212}, {name: s3, value: 0x93131313},
+ {name: s4, value: 0x94141414}, {name: s5, value: 0x95151515},
+ {name: s6, value: 0x96161616}, {name: s7, value: 0x97171717},
+ {name: s8, value: 0x98181818}, {name: s9, value: 0x19191919},
+ {name: s10, value: 0x9a1a1a1a}, {name: s11, value: 0x9b1b1b1b},
+ {name: t3, value: 0x9c1c1c1c}, {name: t4, value: 0x9d1d1d1d},
+ {name: t5, value: 0x9e1e1e1e}, {name: t6, value: 0x9f1f1f1f},
+ {name: pc, value: 0x90202020}
+ ]
diff --git a/lldb/test/API/python_api/basename/Makefile b/lldb/test/API/python_api/basename/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/python_api/basename/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/python_api/basename/TestGetBaseName.py b/lldb/test/API/python_api/basename/TestGetBaseName.py
new file mode 100644
index 000000000000..a3b752e67a35
--- /dev/null
+++ b/lldb/test/API/python_api/basename/TestGetBaseName.py
@@ -0,0 +1,40 @@
+"""
+Test SBFunction::GetBaseName() and SBSymbol::GetBaseName() APIs.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class GetBaseNameTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ TestBase.setUp(self)
+ self.main_source_file = lldb.SBFileSpec("main.cpp")
+
+ @expectedFailureAll(
+ oslist=["windows"],
+ bugnumber="https://github.com/llvm/llvm-project/issues/156861",
+ )
+ def test(self):
+ """Test SBFunction.GetBaseName() and SBSymbol.GetBaseName()"""
+ self.build()
+ _, _, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", self.main_source_file
+ )
+
+ frame0 = thread.GetFrameAtIndex(0)
+
+ # Get both function and symbol
+ function = frame0.GetFunction()
+ symbol = frame0.GetSymbol()
+
+ # Test consistency between function and symbol basename
+ function_basename = function.GetBaseName()
+ symbol_basename = symbol.GetBaseName()
+
+ self.assertEqual(function_basename, "templateFunc")
+ self.assertEqual(symbol_basename, "templateFunc")
diff --git a/lldb/test/API/python_api/basename/main.cpp b/lldb/test/API/python_api/basename/main.cpp
new file mode 100644
index 000000000000..9b4140985cf4
--- /dev/null
+++ b/lldb/test/API/python_api/basename/main.cpp
@@ -0,0 +1,16 @@
+#include <iostream>
+
+namespace ns {
+template <typename T> class MyClass {
+public:
+ void templateFunc() {
+ std::cout << "In templateFunc" << std::endl; // Set a breakpoint here
+ }
+};
+} // namespace ns
+
+int main() {
+ ns::MyClass<int> obj;
+ obj.templateFunc();
+ return 0;
+}
diff --git a/lldb/test/API/python_api/interpreter/TestRunCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestRunCommandInterpreterAPI.py
index f677b869d137..456598785529 100644
--- a/lldb/test/API/python_api/interpreter/TestRunCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestRunCommandInterpreterAPI.py
@@ -105,7 +105,7 @@ class CommandRunInterpreterAPICase(TestBase):
self.assertFalse(has_crashed)
self.assertIn("invalid target", result_str)
- self.assertIn("No auto repeat", result_str)
+ self.assertIn("no auto repeat", result_str)
class SBCommandInterpreterRunOptionsCase(TestBase):
diff --git a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
index 99f88d3da794..b12f4daee0c8 100644
--- a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
+++ b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
@@ -48,10 +48,11 @@ class TestStructuredDataAPI(TestBase):
s.Clear()
error = example.GetDescription(s)
self.assertSuccess(error, "GetDescription works")
+ # Ensure str() doesn't raise an exception.
+ self.assertTrue(str(example))
if not "key_float" in s.GetData():
self.fail("FAILED: could not find key_float in description output")
- dict_struct = lldb.SBStructuredData()
dict_struct = example.GetValueForKey("key_dict")
# Tests for dictionary data type
@@ -113,18 +114,22 @@ class TestStructuredDataAPI(TestBase):
self.assertSuccess(example.SetFromJSON("1"))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeInteger)
self.assertEqual(example.GetIntegerValue(), 1)
+ self.assertEqual(int(example), 1)
self.assertSuccess(example.SetFromJSON("4.19"))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeFloat)
self.assertEqual(example.GetFloatValue(), 4.19)
+ self.assertEqual(float(example), 4.19)
self.assertSuccess(example.SetFromJSON('"Bonjour, 123!"'))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
self.assertEqual(example.GetStringValue(42), "Bonjour, 123!")
+ self.assertEqual(str(example), "Bonjour, 123!")
self.assertSuccess(example.SetFromJSON("true"))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeBoolean)
self.assertTrue(example.GetBooleanValue())
+ self.assertTrue(example)
self.assertSuccess(example.SetFromJSON("null"))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeNull)
@@ -187,38 +192,35 @@ class TestStructuredDataAPI(TestBase):
self.assertEqual(sb_data, example_arr)
def invalid_struct_test(self, example):
- invalid_struct = lldb.SBStructuredData()
invalid_struct = example.GetValueForKey("invalid_key")
if invalid_struct.IsValid():
self.fail("An invalid object should have been returned")
# Check Type API
- if not invalid_struct.GetType() == lldb.eStructuredDataTypeInvalid:
+ if invalid_struct.GetType() != lldb.eStructuredDataTypeInvalid:
self.fail("Wrong type returned: " + str(invalid_struct.GetType()))
def dictionary_struct_test(self, example):
# Check API returning a valid SBStructuredData of 'dictionary' type
- dict_struct = lldb.SBStructuredData()
dict_struct = example.GetValueForKey("key_dict")
if not dict_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not dict_struct.GetType() == lldb.eStructuredDataTypeDictionary:
+ if dict_struct.GetType() != lldb.eStructuredDataTypeDictionary:
self.fail("Wrong type returned: " + str(dict_struct.GetType()))
# Check Size API for 'dictionary' type
- if not dict_struct.GetSize() == 6:
+ if dict_struct.GetSize() != 6:
self.fail("Wrong no of elements returned: " + str(dict_struct.GetSize()))
def string_struct_test(self, dict_struct):
- string_struct = lldb.SBStructuredData()
string_struct = dict_struct.GetValueForKey("key_string")
if not string_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not string_struct.GetType() == lldb.eStructuredDataTypeString:
+ if string_struct.GetType() != lldb.eStructuredDataTypeString:
self.fail("Wrong type returned: " + str(string_struct.GetType()))
# Check API returning 'string' value
@@ -238,18 +240,17 @@ class TestStructuredDataAPI(TestBase):
# Check a valid SBStructuredData containing an unsigned integer.
# We intentionally make this larger than what an int64_t can hold but
# still small enough to fit a uint64_t
- uint_struct = lldb.SBStructuredData()
uint_struct = dict_struct.GetValueForKey("key_uint")
if not uint_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not uint_struct.GetType() == lldb.eStructuredDataTypeInteger:
+ if uint_struct.GetType() != lldb.eStructuredDataTypeInteger:
self.fail("Wrong type returned: " + str(uint_struct.GetType()))
# Check API returning unsigned integer value
output = uint_struct.GetUnsignedIntegerValue()
- if not output == 0xFFFFFFFF00000000:
+ if output != 0xFFFFFFFF00000000:
self.fail("wrong output: " + str(output))
# Calling wrong API on a SBStructuredData
@@ -262,18 +263,17 @@ class TestStructuredDataAPI(TestBase):
# Check a valid SBStructuredData containing an signed integer.
# We intentionally make this smaller than what an uint64_t can hold but
# still small enough to fit a int64_t
- sint_struct = lldb.SBStructuredData()
sint_struct = dict_struct.GetValueForKey("key_sint")
if not sint_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not sint_struct.GetType() == lldb.eStructuredDataTypeSignedInteger:
+ if sint_struct.GetType() != lldb.eStructuredDataTypeSignedInteger:
self.fail("Wrong type returned: " + str(sint_struct.GetType()))
# Check API returning signed integer value
output = sint_struct.GetSignedIntegerValue()
- if not output == -42:
+ if output != -42:
self.fail("wrong output: " + str(output))
# Calling wrong API on a SBStructuredData
@@ -283,28 +283,26 @@ class TestStructuredDataAPI(TestBase):
self.fail("Valid string " + output + " returned for an integer object")
def double_struct_test(self, dict_struct):
- floating_point_struct = lldb.SBStructuredData()
floating_point_struct = dict_struct.GetValueForKey("key_float")
if not floating_point_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not floating_point_struct.GetType() == lldb.eStructuredDataTypeFloat:
+ if floating_point_struct.GetType() != lldb.eStructuredDataTypeFloat:
self.fail("Wrong type returned: " + str(floating_point_struct.GetType()))
# Check API returning 'double' value
output = floating_point_struct.GetFloatValue()
- if not output == 2.99:
+ if output != 2.99:
self.fail("wrong output: " + str(output))
def bool_struct_test(self, dict_struct):
- bool_struct = lldb.SBStructuredData()
bool_struct = dict_struct.GetValueForKey("key_bool")
if not bool_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not bool_struct.GetType() == lldb.eStructuredDataTypeBoolean:
+ if bool_struct.GetType() != lldb.eStructuredDataTypeBoolean:
self.fail("Wrong type returned: " + str(bool_struct.GetType()))
# Check API returning 'bool' value
@@ -314,17 +312,16 @@ class TestStructuredDataAPI(TestBase):
def array_struct_test(self, dict_struct):
# Check API returning a valid SBStructuredData of 'array' type
- array_struct = lldb.SBStructuredData()
array_struct = dict_struct.GetValueForKey("key_array")
if not array_struct.IsValid():
self.fail("A valid object should have been returned")
# Check Type API
- if not array_struct.GetType() == lldb.eStructuredDataTypeArray:
+ if array_struct.GetType() != lldb.eStructuredDataTypeArray:
self.fail("Wrong type returned: " + str(array_struct.GetType()))
# Check Size API for 'array' type
- if not array_struct.GetSize() == 2:
+ if array_struct.GetSize() != 2:
self.fail("Wrong no of elements returned: " + str(array_struct.GetSize()))
# Check API returning a valid SBStructuredData for different 'array'
@@ -332,17 +329,73 @@ class TestStructuredDataAPI(TestBase):
string_struct = array_struct.GetItemAtIndex(0)
if not string_struct.IsValid():
self.fail("A valid object should have been returned")
- if not string_struct.GetType() == lldb.eStructuredDataTypeString:
+ if string_struct.GetType() != lldb.eStructuredDataTypeString:
self.fail("Wrong type returned: " + str(string_struct.GetType()))
output = string_struct.GetStringValue(5)
- if not output == "23":
+ if output != "23":
self.fail("wrong output: " + str(output))
string_struct = array_struct.GetItemAtIndex(1)
if not string_struct.IsValid():
self.fail("A valid object should have been returned")
- if not string_struct.GetType() == lldb.eStructuredDataTypeString:
+ if string_struct.GetType() != lldb.eStructuredDataTypeString:
self.fail("Wrong type returned: " + str(string_struct.GetType()))
output = string_struct.GetStringValue(5)
- if not output == "arr":
+ if output != "arr":
self.fail("wrong output: " + str(output))
+
+ def test_round_trip_scalars(self):
+ for original in (0, 11, -1, 0.0, 4.5, -0.25):
+ constructor = type(original)
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(original))
+ round_tripped = constructor(data)
+ self.assertEqual(round_tripped, original)
+
+ def test_dynamic(self):
+ for original in (0, 11, -1, 0.0, 4.5, -0.25, "", "dirk", True, False):
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(original))
+ self.assertEqual(data.dynamic, original)
+
+ def test_round_trip_int(self):
+ for original in (0, 11, -1):
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(original))
+ self.assertEqual(int(data), int(original))
+
+ def test_round_trip_float(self):
+ for original in (0, 11, -1, 0.0, 4.5, -0.25):
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(original))
+ self.assertEqual(float(data), float(original))
+
+ def test_iterate_array(self):
+ array = [0, 1, 2]
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(array))
+ for value in data:
+ self.assertEqual(value, array.pop(0))
+
+ def test_iterate_dictionary(self):
+ dictionary = {"0": 0, "1": 1, "2": 2}
+ keys = set(dictionary.keys())
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(dictionary))
+ for key in data:
+ self.assertIn(key, keys)
+ keys.remove(key)
+
+ def test_getitem_array(self):
+ array = [1, 2, 3]
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(array))
+ for i in range(len(array)):
+ self.assertEqual(data[i], array[i])
+
+ def test_getitem_dictionary(self):
+ dictionary = {"one": 1, "two": 2, "three": 3}
+ data = lldb.SBStructuredData()
+ data.SetFromJSON(json.dumps(dictionary))
+ for key in dictionary:
+ self.assertEqual(data[key], dictionary[key])
diff --git a/lldb/test/API/riscv/step/TestSoftwareStep.py b/lldb/test/API/riscv/step/TestSoftwareStep.py
index 279c4c1b797e..0544d2102d0f 100644
--- a/lldb/test/API/riscv/step/TestSoftwareStep.py
+++ b/lldb/test/API/riscv/step/TestSoftwareStep.py
@@ -26,19 +26,26 @@ class TestSoftwareStep(TestBase):
substrs=["stopped", "stop reason = instruction step into"],
)
- pc = cur_thread.GetFrameAtIndex(0).GetPC()
+ # Get the instruction we stopped at
+ pc = cur_thread.GetFrameAtIndex(0).GetPCAddress()
+ inst = target.ReadInstructions(pc, 1).GetInstructionAtIndex(0)
- return pc - entry_pc
+ inst_mnemonic = inst.GetMnemonic(target)
+ inst_operands = inst.GetOperands(target)
+ if not inst_operands:
+ return inst_mnemonic
- @skipIf(archs=no_match("^rv.*"))
+ return f"{inst_mnemonic} {inst_operands}"
+
+ @skipIf(archs=no_match("^riscv.*"))
def test_cas(self):
"""
This test verifies LLDB instruction step handling of a proper lr/sc pair.
"""
- difference = self.do_sequence_test("main", "cas")
- self.assertEqual(difference, 0x1A)
+ instruction = self.do_sequence_test("main", "cas")
+ self.assertEqual(instruction, "nop")
- @skipIf(archs=no_match("^rv.*"))
+ @skipIf(archs=no_match("^riscv.*"))
def test_branch_cas(self):
"""
LLDB cannot predict the actual state of registers within a critical section (i.e., inside an atomic
@@ -51,29 +58,29 @@ class TestSoftwareStep(TestBase):
test is nearly identical to the previous one, except for the branch condition, which is inverted and
will result in a taken jump.
"""
- difference = self.do_sequence_test("branch", "branch_cas")
- self.assertEqual(difference, 0x1A)
+ instruction = self.do_sequence_test("branch", "branch_cas")
+ self.assertEqual(instruction, "ret")
- @skipIf(archs=no_match("^rv.*"))
+ @skipIf(archs=no_match("^riscv.*"))
def test_incomplete_sequence_without_lr(self):
"""
This test verifies the behavior of a standalone sc instruction without a preceding lr. Since the sc
lacks the required lr pairing, LLDB should treat it as a non-atomic store rather than part of an
atomic sequence.
"""
- difference = self.do_sequence_test(
+ instruction = self.do_sequence_test(
"incomplete_sequence_without_lr", "incomplete_cas"
)
- self.assertEqual(difference, 0x4)
+ self.assertEqual(instruction, "and a5, a2, a4")
- @skipIf(archs=no_match("^rv.*"))
+ @skipIf(archs=no_match("^riscv.*"))
def test_incomplete_sequence_without_sc(self):
"""
This test checks the behavior of a standalone lr instruction without a subsequent sc. Since the lr
lacks its required sc counterpart, LLDB should treat it as a non-atomic load rather than part of an
atomic sequence.
"""
- difference = self.do_sequence_test(
+ instruction = self.do_sequence_test(
"incomplete_sequence_without_sc", "incomplete_cas"
)
- self.assertEqual(difference, 0x4)
+ self.assertEqual(instruction, "and a5, a2, a4")
diff --git a/lldb/test/API/riscv/step/branch.c b/lldb/test/API/riscv/step/branch.c
index 79bf0ca005db..93d6c51ec75e 100644
--- a/lldb/test/API/riscv/step/branch.c
+++ b/lldb/test/API/riscv/step/branch.c
@@ -11,6 +11,7 @@ void __attribute__((naked)) branch_cas(int *a, int *b) {
"xor a5, a2, a5\n\t"
"sc.w a5, a1, (a3)\n\t"
"beqz a5, 1b\n\t"
+ "nop\n\t"
"2:\n\t"
"ret\n\t");
}
diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
index eda313c6d69c..ceb906f7f50e 100644
--- a/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
+++ b/lldb/test/API/riscv/step/incomplete_sequence_without_lr.c
@@ -1,7 +1,7 @@
void __attribute__((naked)) incomplete_cas(int *a, int *b) {
// Stop at the first instruction (an sc without a corresponding lr), then make
// a step instruction and ensure that execution stops at the next instruction
- // (and).
+ // (and a5, a2, a4).
asm volatile("1:\n\t"
"sc.w a5, a1, (a3)\n\t"
"and a5, a2, a4\n\t"
diff --git a/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
index 952dc7d2804e..3f1bf64b85c5 100644
--- a/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
+++ b/lldb/test/API/riscv/step/incomplete_sequence_without_sc.c
@@ -1,7 +1,7 @@
void __attribute__((naked)) incomplete_cas(int *a, int *b) {
// Stop at the first instruction (an lr without a corresponding sc), then make
// a step instruction and ensure that execution stops at the next instruction
- // (and).
+ // (and a5, a2, a4).
asm volatile("1:\n\t"
"lr.w a2, (a0)\n\t"
"and a5, a2, a4\n\t"
diff --git a/lldb/test/API/riscv/step/main.c b/lldb/test/API/riscv/step/main.c
index 35e8aee2cae4..6649034aa0a3 100644
--- a/lldb/test/API/riscv/step/main.c
+++ b/lldb/test/API/riscv/step/main.c
@@ -1,7 +1,7 @@
void __attribute__((naked)) cas(int *a, int *b) {
// This atomic sequence implements a copy-and-swap function. This test should
- // at the first instruction, and after step instruction, we should stop at the
- // end of the sequence (on the ret instruction).
+ // stop at the first instruction, and after step instruction, we should stop
+ // at the end of the sequence (on the ret instruction).
asm volatile("1:\n\t"
"lr.w a2, (a0)\n\t"
"and a5, a2, a4\n\t"
@@ -11,6 +11,7 @@ void __attribute__((naked)) cas(int *a, int *b) {
"xor a5, a2, a5\n\t"
"sc.w a5, a1, (a3)\n\t"
"beqz a5, 1b\n\t"
+ "nop\n\t"
"2:\n\t"
"ret\n\t");
}
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
index c54e21c1b973..d7d25ca20f85 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -40,6 +40,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_exit()
@skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfWindows # https://github.com/llvm/llvm-project/issues/137660
def test_by_pid(self):
"""
Tests attaching to a process by process ID.
@@ -93,6 +94,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
self.set_and_hit_breakpoint(continueToExit=True)
@skipIfNetBSD # Hangs on NetBSD as well
+ @skipIfWindows
def test_commands(self):
"""
Tests the "initCommands", "preRunCommands", "stopCommands",
diff --git a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
index e722fcea9283..109f34ff10a5 100644
--- a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
+++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
@@ -37,6 +37,7 @@ class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
def async_cancel(self, requestId: int) -> int:
return self.send_async_req(command="cancel", arguments={"requestId": requestId})
+ @skipIfWindows
def test_pending_request(self):
"""
Tests cancelling a pending request.
diff --git a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
index ceddaeb50cd3..202b23abc181 100644
--- a/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
+++ b/lldb/test/API/tools/lldb-dap/console/TestDAP_console.py
@@ -38,6 +38,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
),
)
+ @skipIfWindows
def test_scopes_variables_setVariable_evaluate(self):
"""
Tests that the "scopes" request causes the currently selected
diff --git a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
index 41112a4f5b9e..22fcd42b3d36 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -7,6 +7,7 @@ from lldbsuite.test.lldbtest import *
import lldbdap_testcase
import os
import re
+import tempfile
# Many tests are skipped on Windows because get_stdout() returns None there.
# Despite the test program printing correctly. See
@@ -94,6 +95,8 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
# Check the return code
self.assertEqual(self.dap_server.process.poll(), 0)
+ # Flakey on Windows, https://github.com/llvm/llvm-project/issues/137660.
+ @skipIfWindows
def test_stopOnEntry(self):
"""
Tests the default launch of a simple program that stops at the
@@ -582,3 +585,42 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
version_string.splitlines(),
"version string does not match",
)
+
+ def test_no_lldbinit_flag(self):
+ """
+ Test that the --no-lldbinit flag prevents sourcing .lldbinit files.
+ """
+ # Create a temporary .lldbinit file in the home directory
+ with tempfile.TemporaryDirectory() as temp_home:
+ lldbinit_path = os.path.join(temp_home, ".lldbinit")
+
+ # Write a command to the .lldbinit file that would set a unique setting
+ with open(lldbinit_path, "w") as f:
+ f.write("settings set stop-disassembly-display never\n")
+ f.write("settings set target.x86-disassembly-flavor intel\n")
+
+ # Test with --no-lldbinit flag (should NOT source .lldbinit)
+ self.build_and_create_debug_adapter(
+ lldbDAPEnv={"HOME": temp_home}, additional_args=["--no-lldbinit"]
+ )
+ program = self.getBuildArtifact("a.out")
+
+ # Use initCommands to check if .lldbinit was sourced
+ initCommands = ["settings show stop-disassembly-display"]
+
+ # Launch with initCommands to check the setting
+ self.launch(program, initCommands=initCommands, stopOnEntry=True)
+
+ # Get console output to verify the setting was NOT set from .lldbinit
+ output = self.get_console()
+ self.assertTrue(output and len(output) > 0, "expect console output")
+
+ # Verify the setting has default value, not "never" from .lldbinit
+ self.assertNotIn(
+ "never",
+ output,
+ "Setting should have default value when --no-lldbinit is used",
+ )
+
+ # Verify the initCommands were executed
+ self.verify_commands("initCommands", output, initCommands)
diff --git a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
index e01320c25b15..12b321cf4277 100644
--- a/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
+++ b/lldb/test/API/tools/lldb-dap/server/TestDAP_server.py
@@ -5,6 +5,7 @@ Test lldb-dap server integration.
import os
import signal
import tempfile
+import time
import dap_server
from lldbsuite.test.decorators import *
@@ -13,22 +14,28 @@ import lldbdap_testcase
class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
- def start_server(self, connection):
+ def start_server(
+ self, connection, connection_timeout=None, wait_seconds_for_termination=None
+ ):
log_file_path = self.getBuildArtifact("dap.txt")
(process, connection) = dap_server.DebugAdapterServer.launch(
executable=self.lldbDAPExec,
connection=connection,
+ connection_timeout=connection_timeout,
log_file=log_file_path,
)
def cleanup():
- process.terminate()
+ if wait_seconds_for_termination is not None:
+ process.wait(wait_seconds_for_termination)
+ else:
+ process.terminate()
self.addTearDownHook(cleanup)
return (process, connection)
- def run_debug_session(self, connection, name):
+ def run_debug_session(self, connection, name, sleep_seconds_in_middle=None):
self.dap_server = dap_server.DebugAdapterServer(
connection=connection,
)
@@ -41,6 +48,8 @@ class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
args=[name],
disconnectAutomatically=False,
)
+ if sleep_seconds_in_middle is not None:
+ time.sleep(sleep_seconds_in_middle)
self.set_source_breakpoints(source, [breakpoint_line])
self.continue_to_next_stop()
self.continue_to_exit()
@@ -108,3 +117,47 @@ class TestDAP_server(lldbdap_testcase.DAPTestCaseBase):
self.dap_server.exit_status,
"Process exited before interrupting lldb-dap server",
)
+
+ @skipIfWindows
+ def test_connection_timeout_at_server_start(self):
+ """
+ Test launching lldb-dap in server mode with connection timeout and waiting for it to terminate automatically when no client connects.
+ """
+ self.build()
+ self.start_server(
+ connection="listen://localhost:0",
+ connection_timeout=1,
+ wait_seconds_for_termination=5,
+ )
+
+ @skipIfWindows
+ def test_connection_timeout_long_debug_session(self):
+ """
+ Test launching lldb-dap in server mode with connection timeout and terminating the server after the a long debug session.
+ """
+ self.build()
+ (_, connection) = self.start_server(
+ connection="listen://localhost:0",
+ connection_timeout=1,
+ wait_seconds_for_termination=5,
+ )
+ # The connection timeout should not cut off the debug session
+ self.run_debug_session(connection, "Alice", 1.5)
+
+ @skipIfWindows
+ def test_connection_timeout_multiple_sessions(self):
+ """
+ Test launching lldb-dap in server mode with connection timeout and terminating the server after the last debug session.
+ """
+ self.build()
+ (_, connection) = self.start_server(
+ connection="listen://localhost:0",
+ connection_timeout=1,
+ wait_seconds_for_termination=5,
+ )
+ time.sleep(0.5)
+ # Should be able to connect to the server.
+ self.run_debug_session(connection, "Alice")
+ time.sleep(0.5)
+ # Should be able to connect to the server, because it's still within the connection timeout.
+ self.run_debug_session(connection, "Bob")
diff --git a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
index b487257b6414..1d13bcdd4837 100644
--- a/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
+++ b/lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py
@@ -8,6 +8,7 @@ import lldbdap_testcase
class TestDAP_startDebugging(lldbdap_testcase.DAPTestCaseBase):
+ @skipIfWindows # https://github.com/llvm/llvm-project/issues/137660
def test_startDebugging(self):
"""
Tests the "startDebugging" reverse request. It makes sure that the IDE can
diff --git a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
index 5c055f679aed..3a747c399566 100644
--- a/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
+++ b/lldb/test/API/tools/lldb-dap/step/TestDAP_step.py
@@ -84,6 +84,7 @@ class TestDAP_step(lldbdap_testcase.DAPTestCaseBase):
# only step one thread that is at the breakpoint and stop
break
+ @skipIfWindows
def test_step_over_inlined_function(self):
"""
Test stepping over when the program counter is in another file.
diff --git a/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
index 03b79a805d34..a386afee21eb 100644
--- a/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
+++ b/lldb/test/API/tools/lldb-dap/stepInTargets/TestDAP_stepInTargets.py
@@ -102,6 +102,7 @@ class TestDAP_stepInTargets(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_exit()
@skipIf(archs=["x86", "x86_64"])
+ @skipIfWindows
def test_supported_capability_other_archs(self):
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
diff --git a/lldb/test/API/windows/launch/replace-dll/Makefile b/lldb/test/API/windows/launch/replace-dll/Makefile
new file mode 100644
index 000000000000..22f1051530f8
--- /dev/null
+++ b/lldb/test/API/windows/launch/replace-dll/Makefile
@@ -0,0 +1 @@
+include Makefile.rules
diff --git a/lldb/test/API/windows/launch/replace-dll/TestReplaceDLL.py b/lldb/test/API/windows/launch/replace-dll/TestReplaceDLL.py
new file mode 100644
index 000000000000..afa97cf4afe5
--- /dev/null
+++ b/lldb/test/API/windows/launch/replace-dll/TestReplaceDLL.py
@@ -0,0 +1,62 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+import gc
+import os
+
+
+class ReplaceDllTestCase(TestBase):
+ @skipUnlessWindows
+ def test(self):
+ """
+ Test that LLDB unlocks module files once all references are released.
+ """
+
+ exe = self.getBuildArtifact("a.out")
+ foo = self.getBuildArtifact("foo.dll")
+ bar = self.getBuildArtifact("bar.dll")
+
+ self.build(
+ dictionary={
+ "DYLIB_NAME": "foo",
+ "DYLIB_C_SOURCES": "foo.c",
+ "C_SOURCES": "test.c",
+ }
+ )
+ self.build(
+ dictionary={
+ "DYLIB_ONLY": "YES",
+ "DYLIB_NAME": "bar",
+ "DYLIB_C_SOURCES": "bar.c",
+ }
+ )
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, VALID_TARGET)
+
+ shlib_names = ["foo"]
+ environment = self.registerSharedLibrariesWithTarget(target, shlib_names)
+ process = target.LaunchSimple(
+ None, environment, self.get_process_working_directory()
+ )
+ self.assertEqual(process.GetExitStatus(), 42)
+
+ module = next((m for m in target.modules if "foo" in m.file.basename), None)
+ self.assertIsNotNone(module)
+ self.assertEqual(module.file.fullpath, foo)
+
+ target.RemoveModule(module)
+ del module
+ gc.collect()
+
+ self.dbg.MemoryPressureDetected()
+
+ os.remove(foo)
+ os.rename(bar, foo)
+
+ process = target.LaunchSimple(
+ None, environment, self.get_process_working_directory()
+ )
+ self.assertEqual(process.GetExitStatus(), 43)
diff --git a/lldb/test/API/windows/launch/replace-dll/bar.c b/lldb/test/API/windows/launch/replace-dll/bar.c
new file mode 100644
index 000000000000..7ddde5123483
--- /dev/null
+++ b/lldb/test/API/windows/launch/replace-dll/bar.c
@@ -0,0 +1 @@
+__declspec(dllexport) int foo() { return 43; }
diff --git a/lldb/test/API/windows/launch/replace-dll/foo.c b/lldb/test/API/windows/launch/replace-dll/foo.c
new file mode 100644
index 000000000000..b971fccc6ed9
--- /dev/null
+++ b/lldb/test/API/windows/launch/replace-dll/foo.c
@@ -0,0 +1 @@
+__declspec(dllexport) int foo() { return 42; }
diff --git a/lldb/test/API/windows/launch/replace-dll/test.c b/lldb/test/API/windows/launch/replace-dll/test.c
new file mode 100644
index 000000000000..4c29f7852ae0
--- /dev/null
+++ b/lldb/test/API/windows/launch/replace-dll/test.c
@@ -0,0 +1,3 @@
+int foo(void);
+
+int main() { return foo(); }
diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt
index b786edc46cd2..39462560c4b9 100644
--- a/lldb/test/CMakeLists.txt
+++ b/lldb/test/CMakeLists.txt
@@ -224,6 +224,7 @@ add_lldb_test_dependency(
llvm-pdbutil
llvm-readobj
llvm-ar
+ yaml2macho-core
)
if(TARGET lld)
diff --git a/lldb/test/Shell/Host/TestCustomShell.test b/lldb/test/Shell/Host/TestCustomShell.test
index 0e948a1b1f7f..5d3e90162fb2 100644
--- a/lldb/test/Shell/Host/TestCustomShell.test
+++ b/lldb/test/Shell/Host/TestCustomShell.test
@@ -6,7 +6,7 @@
# XFAIL: system-openbsd
# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out
-# RUN: SHELL=bogus not %lldb %t.out -b -o 'process launch -X 1 --' 2>&1 | FileCheck %s --check-prefix ERROR
+# RUN: env SHELL=bogus not %lldb %t.out -b -o 'process launch -X 1 --' 2>&1 | FileCheck %s --check-prefix ERROR
# RUN: env -i ASAN_OPTIONS='detect_container_overflow=0' %lldb %t.out -b -o 'process launch -X 1 --' 2>&1 | FileCheck %s
# ERROR: error: shell expansion failed
diff --git a/lldb/test/Shell/Process/Optimization.test b/lldb/test/Shell/Process/Optimization.test
index c189d505ef5d..d2d02a74f621 100644
--- a/lldb/test/Shell/Process/Optimization.test
+++ b/lldb/test/Shell/Process/Optimization.test
@@ -1,5 +1,5 @@
Test warnings.
-REQUIRES: shell, system-darwin
+REQUIRES: system-darwin
RUN: %clang_host -O3 %S/Inputs/true.c -std=c99 -g -o %t.exe
RUN: %lldb -o "b main" -o r -o q -b %t.exe 2>&1 | FileCheck %s
diff --git a/lldb/test/Shell/Process/UnsupportedLanguage.test b/lldb/test/Shell/Process/UnsupportedLanguage.test
index d7e6e5de7751..ec5d1bd9bace 100644
--- a/lldb/test/Shell/Process/UnsupportedLanguage.test
+++ b/lldb/test/Shell/Process/UnsupportedLanguage.test
@@ -1,6 +1,9 @@
Test unsupported language warning
-REQUIRES: shell
+TODO: This test does not work on Windows, seemingly because the sed commands
+are not actually doing anything. This should be fixed so we can enable this on
+Windows.
+UNSUPPORTED: system-windows
RUN: %clang_host %S/Inputs/true.c -std=c99 -g -c -S -emit-llvm -o - \
RUN: | sed -e 's/DW_LANG_C99/DW_LANG_Mips_Assembler/g' >%t.ll
diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
index a5e95cf5a898..872b5a7a4d58 100644
--- a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
+++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
@@ -6,11 +6,11 @@
# RUN: %lldb -b -s %s %t.out | FileCheck %s
run
-# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: Integer addition overflowed
+# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: signed integer addition overflow in '2147483647 + 1'
# CHECK-NEXT: frame #1: {{.*}}`main at ubsan_add_overflow.c
bt
-# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed{{.*}}
+# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$signed integer addition overflow in '2147483647 + 1'{{.*}}
# CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c
frame info
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
index 9c0510c34cca..f680158fdf73 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/app_specific_backtrace_crashlog.test
@@ -11,7 +11,7 @@
# CHECK: (lldb) process status --verbose
# CHECK-NEXT: Process 96535 stopped
# CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[artificial]
+# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[synthetic]
# CHECK: Extended Crash Information:
# CHECK: Application Specific Information:
# CHECK-NEXT: CoreFoundation: *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds [0 .. 3]'
@@ -21,21 +21,21 @@
# CHECK: (lldb) thread backtrace --extended true
# CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[artificial]
-# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[artificial]
-# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[artificial]
-# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[artificial]
-# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[artificial]
-# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[artificial]
-# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[artificial]
-# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[artificial]
-# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[artificial]
-# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[artificial]
-# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[artificial]
-# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[artificial]
-# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[artificial]
-# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial]
-# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[artificial]
+# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[synthetic]
+# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[synthetic]
+# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[synthetic]
+# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[synthetic]
+# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[synthetic]
+# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[synthetic]
+# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[synthetic]
+# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[synthetic]
+# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[synthetic]
+# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[synthetic]
+# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic]
+# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[synthetic]
+# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[synthetic]
+# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic]
+# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[synthetic]
# CHECK: thread #4294967295: tid = 0x0001, 0x00000001a0a58418{{.*}}, queue = 'Application Specific Backtrace'
# CHECK-NEXT: frame #0: 0x00000001a0a58418{{.*}}
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
index 3f572c3300c0..9da21d9aab78 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_arm64_register.test
@@ -15,9 +15,9 @@
# CHECK: (lldb) thread backtrace
# CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
# CHECK: (lldb) thread list
# CHECK-NEXT: Process 22511 stopped
@@ -27,20 +27,20 @@
# CHECK: (lldb) bt all
# CHECK: thread #1, queue = 'com.apple.main-thread'
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
# CHECK-NEXT: thread #2
-# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
# CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
# CHECK: (lldb) register read
# CHECK: General Purpose Registers:
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
index 684be2846f78..2509f2e88d9b 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_json.test
@@ -15,9 +15,9 @@
# CHECK: (lldb) thread backtrace
# CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
# CHECK: (lldb) thread list
# CHECK-NEXT: Process 22511 stopped
@@ -27,17 +27,17 @@
# CHECK: (lldb) bt all
# CHECK: thread #1, queue = 'com.apple.main-thread'
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
# CHECK-NEXT: thread #2
-# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
# CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
index 271a4c2aa90f..74ced35fcfae 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/interactive_crashlog_legacy.test
@@ -15,9 +15,9 @@
# CHECK: (lldb) thread backtrace
# CHECK-NEXT: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
# CHECK: (lldb) thread list
# CHECK-NEXT: Process 22511 stopped
@@ -27,17 +27,17 @@
# CHECK: (lldb) bt all
# CHECK: thread #1
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
# CHECK-NEXT: thread #2
-# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
# CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
index a17b7ac18a62..53c0732deb54 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/last_exception_backtrace_crashlog.test
@@ -11,7 +11,7 @@
# CHECK: (lldb) process status --verbose
# CHECK-NEXT: Process 96535 stopped
# CHECK-NEXT: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[artificial]
+# CHECK-NEXT: frame #0: 0x00000001a08c7224{{.*}}[synthetic]
# CHECK: Extended Crash Information:
# CHECK: Application Specific Information:
# CHECK-NEXT: CoreFoundation: *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 10 beyond bounds [0 .. 3]'
@@ -21,21 +21,21 @@
# CHECK: (lldb) thread backtrace --extended true
# CHECK: * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_CRASH (code=0, subcode=0x0)
-# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[artificial]
-# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[artificial]
-# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[artificial]
-# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[artificial]
-# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[artificial]
-# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[artificial]
-# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[artificial]
-# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[artificial]
-# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[artificial]
-# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[artificial]
-# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[artificial]
-# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[artificial]
-# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[artificial]
-# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[artificial]
-# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[artificial]
+# CHECK-NEXT: * frame #0: 0x00000001a08c7224{{.*}}[synthetic]
+# CHECK-NEXT: frame #1: 0x00000001a08fdceb{{.*}}[synthetic]
+# CHECK-NEXT: frame #2: 0x00000001a08372c7{{.*}}[synthetic]
+# CHECK-NEXT: frame #3: 0x00000001a08b7b17{{.*}}[synthetic]
+# CHECK-NEXT: frame #4: 0x00000001a08a7a0b{{.*}}[synthetic]
+# CHECK-NEXT: frame #5: 0x00000001a05ab763{{.*}}[synthetic]
+# CHECK-NEXT: frame #6: 0x00000001a08b6eb3{{.*}}[synthetic]
+# CHECK-NEXT: frame #7: 0x00000001a08b9c2b{{.*}}[synthetic]
+# CHECK-NEXT: frame #8: 0x00000001a08b9bd7{{.*}}[synthetic]
+# CHECK-NEXT: frame #9: 0x00000001a05a3007{{.*}}[synthetic]
+# CHECK-NEXT: frame #10: 0x00000001a0b3dcc3{{.*}}[synthetic]
+# CHECK-NEXT: frame #11: 0x00000001a0b46af3{{.*}}[synthetic]
+# CHECK-NEXT: frame #12: 0x00000001a09a12a3{{.*}}[synthetic]
+# CHECK-NEXT: frame #13: 0x00000001047e3ecf asi`main{{.*}}[synthetic]
+# CHECK-NEXT: frame #14: 0x00000001a05d3e4f{{.*}}[synthetic]
# CHECK: thread #4294967295: tid = 0x0001, 0x00000001a0a5840c{{.*}}, queue = 'Application Specific Backtrace'
# CHECK-NEXT: frame #0: 0x00000001a0a5840c{{.*}}
diff --git a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
index 52a185b8cf76..138cd2bdffc4 100644
--- a/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
+++ b/lldb/test/Shell/ScriptInterpreter/Python/Crashlog/skipped_status_interactive_crashlog.test
@@ -15,9 +15,9 @@ process status
thread backtrace
# CHECK: * thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
thread list
# CHECK: Process 22511 stopped
@@ -27,20 +27,20 @@ thread list
bt all
# CHECK: thread #1, queue = 'com.apple.main-thread'
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [artificial]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc40b84{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5b3b multithread-test`main{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x00000002230f8da7{{.*}} [synthetic]
# CHECK-NEXT: thread #2
-# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: frame #0: 0x000000019cc42c9c{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x0000000100ec5957 multithread-test`call_and_wait{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
# CHECK-NEXT:* thread #3, stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
-# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [artificial]
-# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [artificial]
-# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [artificial]
-# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [artificial]
+# CHECK-NEXT: * frame #0: 0x0000000100ec58f4 multithread-test`bar{{.*}} [synthetic]
+# CHECK-NEXT: frame #1: 0x0000000100ec591b multithread-test`foo{{.*}} [synthetic]
+# CHECK-NEXT: frame #2: 0x0000000100ec5a87 multithread-test`compute_pow{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc7e06b{{.*}} [synthetic]
+# CHECK: frame #{{[0-9]+}}: 0x000000019cc78e2b{{.*}} [synthetic]
image list
# CHECK: 11111111-2222-3333-4444-555555555555 {{.*}}bogus.dylib
diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
index 2692c3d9c3e7..55487235ae8c 100644
--- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
+++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test
@@ -2,8 +2,11 @@
# ${function.return-right} in languages that don't implement this frame
# format variable (in this case Objective-C).
#
+# link.exe will discard DWARF information.
+# REQUIRES: (system-windows && lld) || !system-windows
+#
# RUN: split-file %s %t
-# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out
+# RUN: %clang_host -g -gdwarf %t/main.m -o %t.objc.out %if system-windows %{-fuse-ld=lld%}
# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
# RUN: | FileCheck %s
diff --git a/lldb/test/Shell/SymbolFile/DWARF/deterministic-build.cpp b/lldb/test/Shell/SymbolFile/DWARF/deterministic-build.cpp
index 9e79f23db2b7..0c86107fc963 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/deterministic-build.cpp
+++ b/lldb/test/Shell/SymbolFile/DWARF/deterministic-build.cpp
@@ -3,9 +3,8 @@
// requires the ld64 linker, which clang invokes by default.
// REQUIRES: system-darwin
// RUN: %clang_host %s -g -c -o %t.o
-// RUN: ZERO_AR_DATE=1 %clang_host %t.o -g -o %t
-// RUN: %lldb %t -o "breakpoint set -f %s -l 11" -o run -o exit | FileCheck %s
+// RUN: env ZERO_AR_DATE=1 %clang_host %t.o -g -o %t
+// RUN: %lldb %t -o "breakpoint set -f %s -l 10" -o run -o exit | FileCheck %s
// CHECK: stop reason = breakpoint
-
int main() { return 0; }
diff --git a/lldb/test/Shell/SymbolFile/DWARF/dwo-missing-error.test b/lldb/test/Shell/SymbolFile/DWARF/dwo-missing-error.test
index 2805bbb5df7d..72315e828474 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/dwo-missing-error.test
+++ b/lldb/test/Shell/SymbolFile/DWARF/dwo-missing-error.test
@@ -11,12 +11,12 @@
# "a.out-dwo-missing-error.dwo".
# RUN: rm -rf %t.compdir/
# RUN: mkdir -p %t.compdir/a/b/
-# RUN: cd %t.compdir/a/b/
+# RUN: pushd %t.compdir/a/b/
# RUN: %clang_host %S/Inputs/dwo-missing-error.c -glldb -gdwarf-5 \
# RUN: -gsplit-dwarf -fdebug-prefix-map=%t.compdir=. -o a.out
# RUN: rm *.dwo
# RUN: %lldb a.out -s %s -o exit 2>&1 | FileCheck %s
-# RUN: cd -
+# RUN: popd
# Test the error message with an absolute DW_AT_comp_dir and DW_AT_dwo_name.
# RUN: rm -rf %t.compdir/
diff --git a/lldb/test/Shell/SymbolFile/DWARF/dwo-static-data-member-access.test b/lldb/test/Shell/SymbolFile/DWARF/dwo-static-data-member-access.test
index 6e4deae7b9a0..40d5e90097eb 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/dwo-static-data-member-access.test
+++ b/lldb/test/Shell/SymbolFile/DWARF/dwo-static-data-member-access.test
@@ -4,6 +4,9 @@
# a DW_TAG_variable DIE, whose parent DIE is only
# a forward declaration.
+# UNSUPPORTED: system-darwin
+# UNSUPPORTED: system-windows
+
# RUN: %clangxx_host %S/Inputs/dwo-static-data-member.cpp \
# RUN: -g -gdwarf-5 -gsplit-dwarf -flimit-debug-info -o %t
# RUN: %lldb %t -s %s -o exit 2>&1 | FileCheck %s
diff --git a/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test b/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test
new file mode 100644
index 000000000000..30109c2943c9
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/objcxx-forward-decls.test
@@ -0,0 +1,70 @@
+# REQUIRES: system-darwin
+
+# In this test we have two CUs with conflicting forward declaration
+# depending on the CU language (one is C++ and the other is Objective-C++).
+# We are then stopped in the C++ CU and try to print the type, at which
+# point LLDB will try to make it into an Clang AST node. If LLDB were to
+# interpret the type as C++ instead of Objective-C, we'd violate Clang
+# invariants and crash.
+#
+# RUN: split-file %s %t
+# RUN: %clangxx_host -c -g -x objective-c++ %t/request.m -o %t/request_objc.o
+# RUN: %clangxx_host -c -g %t/main.cpp -o %t/main.o
+# RUN: %clangxx_host %t/main.o %t/request_objc.o -framework Foundation -o %t/a.out
+#
+# RUN: %lldb %t/a.out \
+# RUN: -o "breakpoint set -p return -X main" \
+# RUN: -o run \
+# RUN: -o "frame variable r" \
+# RUN: -o exit | FileCheck %s
+#
+# RUN: dsymutil %t/a.out
+#
+# RUN: %lldb %t/a.out \
+# RUN: -o "breakpoint set -p return -X main" \
+# RUN: -o run \
+# RUN: -o "frame variable r" \
+# RUN: -o exit | FileCheck %s --check-prefix=CHECK-DSYM
+
+# CHECK: (lldb) frame variable r
+# CHECK-NEXT: (Request) ::r = (m_request = "Hello, World!")
+
+# CHECK-DSYM: (lldb) frame variable r
+# CHECK-DSYM-NEXT: (Request) ::r = (m_request = "Hello, World!")
+
+#--- lib.h
+#ifndef LIB_H_IN
+#define LIB_H_IN
+
+#ifdef __OBJC__
+@class NSString;
+#else
+class NSString;
+#endif
+
+struct Request {
+ NSString * m_request = nullptr;
+};
+
+#endif // _H_IN
+
+#--- main.cpp
+#include "lib.h"
+
+void process(Request *);
+
+Request r;
+
+int main() {
+ process(&r);
+ return 0;
+}
+
+#--- request.m
+#import <Foundation/Foundation.h>
+
+#include "lib.h"
+
+void process(Request * r) {
+ r->m_request = @"Hello, World!";
+}
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
index c93033890544..d08f49d1014b 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/Inputs/incomplete-tag-type.cpp
@@ -13,3 +13,8 @@ struct E {
E();
};
E::E() = default;
+
+struct I {
+ I();
+};
+I::I() = default;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp
deleted file mode 100644
index 7bc7e618667f..000000000000
--- a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-// clang-format off
-// REQUIRES: lld, x86
-
-// RUN: %clang_cl --target=x86_64-windows-msvc -c /Fo%t1.obj -- %p/Inputs/incomplete-tag-type.cpp
-// RUN: %clang_cl --target=x86_64-windows-msvc /O1 /Z7 -c /Fo%t2.obj -- %s
-// RUN: lld-link /debug:full /nodefaultlib /entry:main %t1.obj %t2.obj /out:%t.exe /pdb:%t.pdb
-// RUN: %lldb -f %t.exe -o \
-// RUN: "settings set interpreter.stop-command-source-on-error false" \
-// RUN: -o "expression b" -o "expression d" -o "expression static_e_ref" -o "exit" 2>&1 | FileCheck %s
-
-// CHECK: (lldb) expression b
-// CHECK: (B) $0 = {}
-// CHECK: (lldb) expression d
-// CHECK: (D) $1 = {}
-// CHECK: (lldb) expression static_e_ref
-// CHECK: error:{{.*}}incomplete type 'E' where a complete type is required
-
-// Complete base class.
-struct A { int x; A(); };
-struct B : A {};
-B b;
-
-// Complete data member.
-struct C {
- C();
-};
-
-struct D {
- C c;
-};
-D d;
-
-// Incomplete static data member should return error.
-struct E {
- E();
-};
-
-struct F {
- static E static_e;
-};
-
-E F::static_e = E();
-E& static_e_ref = F::static_e;
-
-int main(){}
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test
new file mode 100644
index 000000000000..f30866ccdd6f
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/incomplete-tag-type.test
@@ -0,0 +1,109 @@
+# REQUIRES: lld, x86
+
+# RUN: split-file %s %t
+
+# RUN: %clang_cl --target=x86_64-windows-msvc -c /Fo%t1.obj -- %p/Inputs/incomplete-tag-type.cpp
+# RUN: %clang_cl --target=x86_64-windows-msvc /O1 /Z7 -c /Fo%t2.obj -- %t/main.cpp
+# RUN: lld-link /debug:full /nodefaultlib /entry:main %t1.obj %t2.obj /out:%t.exe /pdb:%t.pdb
+
+# RUN: %lldb -f %t.exe -s %t/target-var.input 2>&1 | FileCheck %s --check-prefix=TARGET-VAR
+# RUN: %lldb -f %t.exe -s %t/expr.input 2>&1 | FileCheck %s --check-prefix=EXPR
+
+#--- main.cpp
+
+// Complete base class.
+struct A { int x; A(); };
+struct B : A {};
+B b;
+
+// Complete data member.
+struct C {
+ C();
+};
+
+struct D {
+ C c;
+};
+D d;
+
+// Incomplete static data member should return error.
+struct E {
+ E();
+};
+
+struct F {
+ static E static_e;
+};
+
+E F::static_e = E();
+E& static_e_ref = F::static_e;
+
+struct G {
+ int foo = 1;
+};
+struct H {
+ G g[2];
+};
+H h;
+
+struct I {
+ I();
+};
+struct J {
+ I i[2];
+};
+J j;
+
+
+int main(){}
+
+#--- target-var.input
+
+target variable b
+target variable d
+target variable h
+target variable j
+target variable static_e_ref
+exit
+
+#--- expr.input
+
+settings set interpreter.stop-command-source-on-error false
+expression b
+expression d
+expression h
+expression j
+expression static_e_ref
+exit
+
+# TARGET-VAR: (lldb) target variable b
+# TARGET-VAR-NEXT: (B) b = (A = <incomplete type>)
+# TARGET-VAR-NEXT: (lldb) target variable d
+# TARGET-VAR-NEXT: (D) d = {}
+# TARGET-VAR-NEXT: (lldb) target variable h
+# TARGET-VAR-NEXT: (H) h = {
+# TARGET-VAR-NEXT: g = {
+# TARGET-VAR-NEXT: [0] = (foo = 1)
+# TARGET-VAR-NEXT: [1] = (foo = 1)
+# TARGET-VAR-NEXT: }
+# TARGET-VAR-NEXT: }
+# TARGET-VAR-NEXT: (lldb) target variable j
+# TARGET-VAR-NEXT: (J) j = {}
+# TARGET-VAR-NEXT: (lldb) target variable static_e_ref
+# TARGET-VAR-NEXT: (E &) static_e_ref = 0x{{.*}} <incomplete type "E">
+
+# EXPR: (lldb) expression b
+# EXPR-NEXT: (B) $0 = {}
+# EXPR-NEXT: (lldb) expression d
+# EXPR-NEXT: (D) $1 = {}
+# EXPR-NEXT: (lldb) expression h
+# EXPR-NEXT: (H) $2 = {
+# EXPR-NEXT: g = {
+# EXPR-NEXT: [0] = (foo = 1)
+# EXPR-NEXT: [1] = (foo = 1)
+# EXPR-NEXT: }
+# EXPR-NEXT: }
+# EXPR-NEXT: (lldb) expression j
+# EXPR-NEXT: (J) $3 = {}
+# EXPR-NEXT: (lldb) expression static_e_ref
+# EXPR: error:{{.*}}incomplete type 'E' where a complete type is required
diff --git a/lldb/test/Shell/SymbolFile/PDB/expressions.test b/lldb/test/Shell/SymbolFile/PDB/expressions.test
index 1932be74ca87..33b02e2d6796 100644
--- a/lldb/test/Shell/SymbolFile/PDB/expressions.test
+++ b/lldb/test/Shell/SymbolFile/PDB/expressions.test
@@ -1,6 +1,7 @@
REQUIRES: target-windows, msvc
RUN: %build --compiler=msvc --nodefaultlib --output=%t.exe %S/Inputs/ExpressionsTest.cpp
-RUN: not %lldb -b -s %S/Inputs/ExpressionsTest0.script -s %S/Inputs/ExpressionsTest1.script -s %S/Inputs/ExpressionsTest2.script -- %t.exe 2>&1 | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=0 not %lldb -b -s %S/Inputs/ExpressionsTest0.script -s %S/Inputs/ExpressionsTest1.script -s %S/Inputs/ExpressionsTest2.script -- %t.exe 2>&1 | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 not %lldb -b -s %S/Inputs/ExpressionsTest0.script -s %S/Inputs/ExpressionsTest1.script -s %S/Inputs/ExpressionsTest2.script -- %t.exe 2>&1 | FileCheck %s
// Check the variable value through `expression`
CHECK: (lldb) expression result
diff --git a/lldb/test/Shell/SymbolFile/PDB/variables.test b/lldb/test/Shell/SymbolFile/PDB/variables.test
index 9ee10f75c7e3..970d714c29c3 100644
--- a/lldb/test/Shell/SymbolFile/PDB/variables.test
+++ b/lldb/test/Shell/SymbolFile/PDB/variables.test
@@ -2,15 +2,27 @@ REQUIRES: system-windows, msvc
RUN: mkdir -p %t.dir
RUN: %build --compiler=clang-cl --mode=compile --arch=64 --nodefaultlib --output=%t.dir/VariablesTest.cpp.obj %S/Inputs/VariablesTest.cpp
RUN: %build --compiler=msvc --mode=link --arch=64 --nodefaultlib --output=%t.dir/VariablesTest.cpp.exe %t.dir/VariablesTest.cpp.obj
-RUN: lldb-test symbols %t.dir/VariablesTest.cpp.exe > %t.dir/VariablesTest.out
-RUN: FileCheck --check-prefix=GLOBALS --input-file=%t.dir/VariablesTest.out %s
-RUN: FileCheck --check-prefix=FUNC-F --input-file=%t.dir/VariablesTest.out %s
-RUN: FileCheck --check-prefix=FUNC-MAIN --input-file=%t.dir/VariablesTest.out %s
-RUN: FileCheck --check-prefix=FUNC-CONSTRUCTOR --input-file=%t.dir/VariablesTest.out %s
-RUN: FileCheck --check-prefix=FUNC-MEMBER --input-file=%t.dir/VariablesTest.out %s
+# Note: The native plugin creates a location list for variables that's only valid for the function.
+# The DIA plugin creates a location expression that's always valid. This causes DIA to output
+# one line per variable where the native plugin would output two (the second would contain the
+# location information). This removes the second line from the output of the native plugin.
+# It's done in both cases, because LLDB might not be compiled with the DIA SDK in which case
+# the native plugin is always used.
+RUN: env LLDB_USE_NATIVE_PDB_READER=0 lldb-test symbols %t.dir/VariablesTest.cpp.exe | sed '/^ \+\[0x/d' > %t.dir/VariablesTest.DIA.out
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/VariablesTest.cpp.exe | sed '/^ \+\[0x/d' > %t.dir/VariablesTest.Native.out
+RUN: FileCheck --check-prefix=GLOBALS --input-file=%t.dir/VariablesTest.DIA.out %s
+RUN: FileCheck --check-prefix=GLOBALS --input-file=%t.dir/VariablesTest.Native.out %s
+RUN: FileCheck --check-prefix=FUNC-F --input-file=%t.dir/VariablesTest.DIA.out %s
+RUN: FileCheck --check-prefix=FUNC-F --input-file=%t.dir/VariablesTest.Native.out %s
+RUN: FileCheck --check-prefix=FUNC-MAIN --input-file=%t.dir/VariablesTest.DIA.out %s
+RUN: FileCheck --check-prefix=FUNC-MAIN --input-file=%t.dir/VariablesTest.Native.out %s
+RUN: FileCheck --check-prefix=FUNC-CONSTRUCTOR --input-file=%t.dir/VariablesTest.DIA.out %s
+RUN: FileCheck --check-prefix=FUNC-CONSTRUCTOR --input-file=%t.dir/VariablesTest.Native.out %s
+RUN: FileCheck --check-prefix=FUNC-MEMBER --input-file=%t.dir/VariablesTest.DIA.out %s
+RUN: FileCheck --check-prefix=FUNC-MEMBER --input-file=%t.dir/VariablesTest.Native.out %s
GLOBALS: Module [[MOD:.*]]
-GLOBALS: SymbolFile pdb ([[MOD]])
+GLOBALS: SymbolFile {{(native-)?}}pdb ([[MOD]])
GLOBALS: CompileUnit{{.*}}, language = "c++", file = '{{.*}}\VariablesTest.cpp'
GLOBALS-DAG: Variable{{.*}}, name = "g_IntVar"
GLOBALS-SAME: scope = global, location = {{.*}}, external
@@ -30,7 +42,7 @@ GLOBALS-DAG: Variable{{.*}}, name = "g_Const"
GLOBALS-SAME: scope = ??? (2)
GLOBALS: Function
-FUNC-F: Function{{.*}}, mangled = ?f@@YAHHH@Z
+FUNC-F: Function{{.*}}, {{mangled = \?f@@YAHHH@Z|demangled = f}}
FUNC-F-NEXT: Block
FUNC-F-NEXT: Variable{{.*}}, name = "var_arg1"
FUNC-F-SAME: scope = parameter
@@ -39,7 +51,7 @@ FUNC-F-SAME: scope = parameter
FUNC-F-NEXT: Variable{{.*}}, name = "same_name_var"
FUNC-F-SAME: scope = local
-FUNC-MAIN: Function{{.*}}, mangled = main
+FUNC-MAIN: Function{{.*}}, {{(de)?}}mangled = main
FUNC-MAIN-NEXT: Block
FUNC-MAIN-NEXT: Variable{{.*}}, name = "same_name_var"
FUNC-MAIN-SAME: scope = local
@@ -52,11 +64,10 @@ FUNC-MAIN-SAME: scope = local
FUNC-MAIN-NEXT: Variable{{.*}}, name = "a"
FUNC-MAIN-SAME: scope = local
-FUNC-CONSTRUCTOR: Function{{.*}}, {{(de)?}}mangled = {{.*}}{{(Class::)?}}Class{{.*}}
+FUNC-CONSTRUCTOR: Function{{.*}}, {{(de)?}}mangled = {{.*}}Class::Class{{.*}}
FUNC-CONSTRUCTOR-NEXT: Block
FUNC-CONSTRUCTOR-NEXT: Variable{{.*}}, name = "this"
FUNC-CONSTRUCTOR-SAME: scope = parameter
-FUNC-CONSTRUCTOR-SAME: artificial
FUNC-CONSTRUCTOR-NEXT: Variable{{.*}}, name = "a"
FUNC-CONSTRUCTOR-SAME: scope = parameter
@@ -64,4 +75,3 @@ FUNC-MEMBER: Function{{.*}}, {{(de)?}}mangled = {{.*}}{{(Class::)?}}Func{{.
FUNC-MEMBER-NEXT: Block
FUNC-MEMBER-NEXT: Variable{{.*}}, name = "this"
FUNC-MEMBER-SAME: scope = parameter
-FUNC-MEMBER-SAME: artificial
diff --git a/lldb/test/Shell/SymbolFile/add-dsym.test b/lldb/test/Shell/SymbolFile/add-dsym.test
index 52d1a1363fee..9695ddc297aa 100644
--- a/lldb/test/Shell/SymbolFile/add-dsym.test
+++ b/lldb/test/Shell/SymbolFile/add-dsym.test
@@ -4,5 +4,5 @@
# HELP: Syntax: add-dsym <cmd-options> <filename>
# RUN: yaml2obj %S/Inputs/a.yaml -o %t.out
-# RUN: LLDB_APPLE_DSYMFORUUID_EXECUTABLE=%S/Inputs/dsymforuuid.sh %lldb %t.out -o 'add-dsym -u 41945CA4-5D9D-3CDE-82B4-37E4C09750B5' 2>&1 | FileCheck %s
+# RUN: env LLDB_APPLE_DSYMFORUUID_EXECUTABLE=%S/Inputs/dsymforuuid.sh %lldb %t.out -o 'add-dsym -u 41945CA4-5D9D-3CDE-82B4-37E4C09750B5' 2>&1 | FileCheck %s
# CHECK: UUID information was not found
diff --git a/lldb/test/Shell/Symtab/symtab-wasm.test b/lldb/test/Shell/Symtab/symtab-wasm.test
index 4170d9aba9ee..524691b89732 100644
--- a/lldb/test/Shell/Symtab/symtab-wasm.test
+++ b/lldb/test/Shell/Symtab/symtab-wasm.test
@@ -1,15 +1,16 @@
# RUN: yaml2obj %S/Inputs/simple.wasm.yaml -o %t.wasm
-# RUN: %lldb %t.wasm -o 'image dump symtab' -o 'image dump sections' | FileCheck %s
-CHECK: Code 0x0000000000000002 0x0000000000000002 0x00000000 __wasm_call_ctors
-CHECK: Code 0x0000000000000005 0x0000000000000029 0x00000000 add
-CHECK: Code 0x000000000000002f 0x000000000000004c 0x00000000 __original_main
-CHECK: Code 0x000000000000007c 0x0000000000000009 0x00000000 main
-CHECK: Data 0x0000000000000233 0x0000000000000009 0x00000000 .rodata
-CHECK: Data 0x0000000000000242 0x0000000000000004 0x00000000 .data
+# RUN: %lldb %t.wasm -o 'image dump symtab' | FileCheck %s --check-prefix SYMTAB
+SYMTAB: Code 0x0000000000000002 0x0000000000000002 0x00000000 __wasm_call_ctors
+SYMTAB: Code 0x0000000000000005 0x0000000000000029 0x00000000 add
+SYMTAB: Code 0x000000000000002f 0x000000000000004c 0x00000000 __original_main
+SYMTAB: Code 0x000000000000007c 0x0000000000000009 0x00000000 main
-CHECK: 0x0000000000000001 code {{.*}} 0x000001a1 0x00000085 0x00000000 symtab-wasm.test.tmp.wasm.code
-CHECK: 0x0000000000000003 data {{.*}} 0x0000022c 0x0000001a 0x00000000 symtab-wasm.test.tmp.wasm.data
-CHECK: 0x0000000000000040 wasm-name {{.*}} 0x00000251 0x00000059 0x00000000 symtab-wasm.test.tmp.wasm.name
-CHECK: 0x0000000000000100 data {{.*}} 0x00000233 0x00000009 0x00000000 symtab-wasm.test.tmp.wasm.data..rodata
-CHECK: 0x0000000000000200 data {{.*}} 0x00000242 0x00000004 0x00000000 symtab-wasm.test.tmp.wasm.data..data
+# RUN: %lldb %t.wasm -o 'image dump sections' | FileCheck %s --check-prefix SECTIONS
+SECTIONS: 0x0000000000000001 code [0x0000000000000000-0x0000000000000085) --- 0x000001a1 0x00000085 0x00000000 symtab-wasm.test.tmp.wasm.code
+SECTIONS: 0x0000000000000040 wasm-name --- 0x00000251 0x00000059 0x00000000 symtab-wasm.test.tmp.wasm.name
+SECTIONS: 0x0000000000000100 data [0x0000000000000400-0x0000000000000409) --- 0x00000233 0x00000009 0x00000000 symtab-wasm.test.tmp.wasm..rodata
+SECTIONS: 0x0000000000000200 data [0x000000000000040c-0x0000000000000410) --- 0x00000242 0x00000004 0x00000000 symtab-wasm.test.tmp.wasm..data
+
+# RUN: %lldb %t.wasm -o 'x/s 0x0000000000000400' | FileCheck %s --check-prefix STR
+STR: "data str"
diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py
index cfa5506e4864..46e2117cdb8e 100644
--- a/lldb/test/Shell/lit.cfg.py
+++ b/lldb/test/Shell/lit.cfg.py
@@ -21,7 +21,15 @@ from helper import toolchain
config.name = "lldb-shell"
# testFormat: The test format to use to interpret tests.
-config.test_format = toolchain.ShTestLldb(not llvm_config.use_lit_shell)
+# We prefer the lit internal shell which provides a better user experience on
+# failures and is faster unless the user explicitly disables it with
+# LIT_USE_INTERNAL_SHELL=0 env var.
+use_lit_shell = True
+lit_shell_env = os.environ.get("LIT_USE_INTERNAL_SHELL")
+if lit_shell_env:
+ use_lit_shell = lit.util.pythonize_bool(lit_shell_env)
+
+config.test_format = toolchain.ShTestLldb(not use_lit_shell)
# suffixes: A list of file extensions to treat as test files. This is overriden
# by individual lit.local.cfg files in the test subdirectories.
diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt
index e2f039527ad7..4b54c1a50eb2 100644
--- a/lldb/tools/CMakeLists.txt
+++ b/lldb/tools/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory(lldb-fuzzer EXCLUDE_FROM_ALL)
add_lldb_tool_subdirectory(lldb-instr)
add_lldb_tool_subdirectory(lldb-dap)
+add_lldb_tool_subdirectory(lldb-mcp)
if (LLDB_BUILD_LLDBRPC)
add_lldb_tool_subdirectory(lldb-rpc-gen)
endif()
@@ -27,3 +28,5 @@ endif()
if (LLDB_CAN_USE_LLDB_SERVER)
add_lldb_tool_subdirectory(lldb-server)
endif()
+
+add_lldb_tool_subdirectory(yaml2macho-core)
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
index fd2ac64ac6cf..8ae9d4df9965 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
@@ -877,6 +877,17 @@ void *MachTask::ExceptionThread(void *arg) {
if (exception_message.CatchExceptionRaise(task)) {
if (exception_message.state.task_port != task) {
if (exception_message.state.IsValid()) {
+ pid_t new_pid = -1;
+ kern_return_t kr =
+ pid_for_task(exception_message.state.task_port, &new_pid);
+ pid_t old_pid = mach_proc->ProcessID();
+ if (kr == KERN_SUCCESS && old_pid != new_pid) {
+ DNBLogError("Got an exec mach message but the pid of "
+ "the new task and the pid of the old task "
+ "do not match, something is wrong.");
+ // exit the thread.
+ break;
+ }
// We exec'ed and our task port changed on us.
DNBLogThreadedIf(LOG_EXCEPTIONS,
"task port changed from 0x%4.4x to 0x%4.4x",
diff --git a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
index 6ee1466612ee..e30e02a91152 100644
--- a/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/arm64/DNBArchImplARM64.cpp
@@ -199,6 +199,24 @@ uint64_t DNBArchMachARM64::GetSP(uint64_t failValue) {
return failValue;
}
+static void log_signed_registers(arm_thread_state64_t *gpr, const char *desc) {
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
+ const char *log_str = "%s signed regs "
+ "\n fp=%16.16llx"
+ "\n lr=%16.16llx"
+ "\n sp=%16.16llx"
+ "\n pc=%16.16llx";
+#if defined(DEBUGSERVER_IS_ARM64E)
+ DNBLogThreaded(log_str, desc, reinterpret_cast<uint64_t>(gpr->__opaque_fp),
+ reinterpret_cast<uint64_t>(gpr->__opaque_lr),
+ reinterpret_cast<uint64_t>(gpr->__opaque_sp),
+ reinterpret_cast<uint64_t>(gpr->__opaque_pc));
+#else
+ DNBLogThreaded(log_str, desc, gpr->__fp, gpr->__lr, gpr->__sp, gpr->__pc);
+#endif
+ }
+}
+
kern_return_t DNBArchMachARM64::GetGPRState(bool force) {
int set = e_regSetGPR;
// Check if we have valid cached registers
@@ -210,25 +228,29 @@ kern_return_t DNBArchMachARM64::GetGPRState(bool force) {
kern_return_t kret =
::thread_get_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64,
(thread_state_t)&m_state.context.gpr, &count);
- if (DNBLogEnabledForAny(LOG_THREAD)) {
- uint64_t *x = &m_state.context.gpr.__x[0];
+ log_signed_registers(&m_state.context.gpr, "Values from thread_get_state");
- const char *log_str = "thread_get_state signed regs "
- "\n fp=%16.16llx"
- "\n lr=%16.16llx"
- "\n sp=%16.16llx"
- "\n pc=%16.16llx";
-#if defined(DEBUGSERVER_IS_ARM64E)
- DNBLogThreaded(log_str,
- reinterpret_cast<uint64_t>(m_state.context.gpr.__opaque_fp),
- reinterpret_cast<uint64_t>(m_state.context.gpr.__opaque_lr),
- reinterpret_cast<uint64_t>(m_state.context.gpr.__opaque_sp),
- reinterpret_cast<uint64_t>(m_state.context.gpr.__opaque_pc));
-#else
- DNBLogThreaded(log_str, m_state.context.gpr.__fp, m_state.context.gpr.__lr,
- m_state.context.gpr.__sp, m_state.context.gpr.__pc);
-#endif
+#if defined(THREAD_CONVERT_THREAD_STATE_TO_SELF) && defined(__LP64__)
+ if (kret == KERN_SUCCESS) {
+ mach_msg_type_number_t newcount = ARM_THREAD_STATE64_COUNT;
+ arm_thread_state64_t new_gpr;
+ kern_return_t convert_kret = thread_convert_thread_state(
+ m_thread->MachPortNumber(), THREAD_CONVERT_THREAD_STATE_TO_SELF,
+ ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, count,
+ (thread_state_t)&new_gpr, &newcount);
+ DNBLogThreadedIf(
+ LOG_THREAD,
+ "converted register values "
+ "to debugserver's keys, return value %d, old count %d new count %d",
+ convert_kret, count, newcount);
+ if (convert_kret == KERN_SUCCESS)
+ memcpy(&m_state.context.gpr, &new_gpr, count * 4);
+ log_signed_registers(&m_state.context.gpr,
+ "Values after thread_convert_thread_state");
+ }
+#endif // THREAD_CONVERT_THREAD_STATE_TO_SELF
+ if (DNBLogEnabledForAny(LOG_THREAD)) {
#if defined(DEBUGSERVER_IS_ARM64E)
uint64_t log_fp = clear_pac_bits(
reinterpret_cast<uint64_t>(m_state.context.gpr.__opaque_fp));
@@ -244,6 +266,7 @@ kern_return_t DNBArchMachARM64::GetGPRState(bool force) {
uint64_t log_sp = m_state.context.gpr.__sp;
uint64_t log_pc = m_state.context.gpr.__pc;
#endif
+ uint64_t *x = &m_state.context.gpr.__x[0];
DNBLogThreaded(
"thread_get_state(0x%4.4x, %u, &gpr, %u) => 0x%8.8x (count = %u) regs"
"\n x0=%16.16llx"
@@ -567,10 +590,28 @@ kern_return_t DNBArchMachARM64::GetSMEState(bool force) {
}
kern_return_t DNBArchMachARM64::SetGPRState() {
+ arm_thread_state64_t *state_to_set = &m_state.context.gpr;
+#if defined(THREAD_CONVERT_THREAD_STATE_FROM_SELF) && defined(__LP64__)
+ mach_msg_type_number_t count = ARM_THREAD_STATE64_COUNT;
+ mach_msg_type_number_t new_count = ARM_THREAD_STATE64_COUNT;
+ arm_thread_state64_t new_gpr;
+ memcpy(&new_gpr, &m_state.context.gpr, count * 4);
+ kern_return_t convert_kret = thread_convert_thread_state(
+ m_thread->MachPortNumber(), THREAD_CONVERT_THREAD_STATE_FROM_SELF,
+ ARM_THREAD_STATE64, (thread_state_t)&m_state.context.gpr, count,
+ (thread_state_t)&new_gpr, &new_count);
+ if (convert_kret == KERN_SUCCESS)
+ state_to_set = &new_gpr;
+ DNBLogThreadedIf(LOG_THREAD,
+ "converted register values "
+ "to inferior's keys, return value %d, count %d",
+ convert_kret, new_count);
+#endif // THREAD_CONVERT_THREAD_STATE_TO_SELF
+
int set = e_regSetGPR;
- kern_return_t kret = ::thread_set_state(
- m_thread->MachPortNumber(), ARM_THREAD_STATE64,
- (thread_state_t)&m_state.context.gpr, e_regSetGPRCount);
+ kern_return_t kret =
+ ::thread_set_state(m_thread->MachPortNumber(), ARM_THREAD_STATE64,
+ (thread_state_t)state_to_set, e_regSetGPRCount);
m_state.SetError(set, Write,
kret); // Set the current write error for this register set
m_state.InvalidateRegisterSetState(set); // Invalidate the current register
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index 102b2ab3e848..d9fb22c6a1c0 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -4423,12 +4423,12 @@ rnb_err_t RNBRemote::HandlePacket_qSpeedTest(const char *p) {
return HandlePacket_ILLFORMED(
__FILE__, __LINE__, p,
"Didn't find response_size value at right offset");
- else if (*end == ';') {
- static char g_data[4 * 1024 * 1024 + 16];
- strcpy(g_data, "data:");
- memset(g_data + 5, 'a', response_size);
- g_data[response_size + 5] = '\0';
- return SendPacket(g_data);
+ else if (*end == ';' && response_size < (4 * 1024 * 1024)) {
+ std::vector<char> buf(response_size + 6, 'a');
+ memcpy(buf.data(), "data:", 5);
+ buf[buf.size() - 1] = '\0';
+ rnb_err_t return_value = SendPacket(buf.data());
+ return return_value;
} else {
return SendErrorPacket("E79");
}
diff --git a/lldb/tools/driver/Options.td b/lldb/tools/driver/Options.td
index 1d8372c4aa40..311e2602cc41 100644
--- a/lldb/tools/driver/Options.td
+++ b/lldb/tools/driver/Options.td
@@ -1,233 +1,248 @@
include "llvm/Option/OptParser.td"
-class F<string name>: Flag<["--", "-"], name>;
-class S<string name>: Separate<["--", "-"], name>;
+class F<string name> : Flag<["--", "-"], name>;
+class S<string name> : Separate<["--", "-"], name>;
class R<list<string> prefixes, string name>
- : Option<prefixes, name, KIND_REMAINING_ARGS>;
+ : Option<prefixes, name, KIND_REMAINING_ARGS>;
// Please keep this in sync with the man page in docs/man/lldb.rst
// Attaching options.
def grp_attach : OptionGroup<"attaching">, HelpText<"ATTACHING">;
-def attach_name: Separate<["--", "-"], "attach-name">,
- MetaVarName<"<name>">,
- HelpText<"Tells the debugger to attach to a process with the given name.">,
- Group<grp_attach>;
-def: Separate<["-"], "n">,
- Alias<attach_name>,
- HelpText<"Alias for --attach-name">,
- Group<grp_attach>;
+def attach_name
+ : Separate<["--", "-"], "attach-name">,
+ MetaVarName<"<name>">,
+ HelpText<
+ "Tells the debugger to attach to a process with the given name.">,
+ Group<grp_attach>;
+def : Separate<["-"], "n">,
+ Alias<attach_name>,
+ HelpText<"Alias for --attach-name">,
+ Group<grp_attach>;
def wait_for
: F<"wait-for">,
HelpText<"Tells the debugger to wait for the process with the name "
"specified by --attach-name to launch before attaching.">,
Group<grp_attach>;
-def: Flag<["-"], "w">,
- Alias<wait_for>,
- HelpText<"Alias for --wait-for">,
- Group<grp_attach>;
-
-def attach_pid: Separate<["--", "-"], "attach-pid">,
- MetaVarName<"<pid>">,
- HelpText<"Tells the debugger to attach to a process with the given pid.">,
- Group<grp_attach>;
-def: Separate<["-"], "p">,
- Alias<attach_pid>,
- HelpText<"Alias for --attach-pid">,
- Group<grp_attach>;
+def : Flag<["-"], "w">,
+ Alias<wait_for>,
+ HelpText<"Alias for --wait-for">,
+ Group<grp_attach>;
+def attach_pid
+ : Separate<["--", "-"], "attach-pid">,
+ MetaVarName<"<pid>">,
+ HelpText<"Tells the debugger to attach to a process with the given pid.">,
+ Group<grp_attach>;
+def : Separate<["-"], "p">,
+ Alias<attach_pid>,
+ HelpText<"Alias for --attach-pid">,
+ Group<grp_attach>;
// Scripting options.
def grp_scripting : OptionGroup<"scripting">, HelpText<"SCRIPTING">;
-def python_path: F<"python-path">,
- HelpText<"Prints out the path to the lldb.py file for this version of lldb.">,
- Group<grp_scripting>;
-def: Flag<["-"], "P">,
- Alias<python_path>,
- HelpText<"Alias for --python-path">,
- Group<grp_scripting>;
-
-def print_script_interpreter_info: F<"print-script-interpreter-info">,
- HelpText<"Prints out a json dictionary with information about the scripting language interpreter.">,
- Group<grp_scripting>;
-
-def script_language: Separate<["--", "-"], "script-language">,
- MetaVarName<"<language>">,
- HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,
- Group<grp_scripting>;
-def: Separate<["-"], "l">,
- Alias<script_language>,
- HelpText<"Alias for --script-language">,
- Group<grp_scripting>;
+def python_path
+ : F<"python-path">,
+ HelpText<
+ "Prints out the path to the lldb.py file for this version of lldb.">,
+ Group<grp_scripting>;
+def : Flag<["-"], "P">,
+ Alias<python_path>,
+ HelpText<"Alias for --python-path">,
+ Group<grp_scripting>;
+
+def print_script_interpreter_info
+ : F<"print-script-interpreter-info">,
+ HelpText<"Prints out a json dictionary with information about the "
+ "scripting language interpreter.">,
+ Group<grp_scripting>;
+
+def script_language : Separate<["--", "-"], "script-language">,
+ MetaVarName<"<language>">,
+ HelpText<"Tells the debugger to use the specified "
+ "scripting language for user-defined scripts.">,
+ Group<grp_scripting>;
+def : Separate<["-"], "l">,
+ Alias<script_language>,
+ HelpText<"Alias for --script-language">,
+ Group<grp_scripting>;
// Repl options.
def grp_repl : OptionGroup<"repl">, HelpText<"REPL">;
-def repl: Flag<["--", "-"], "repl">,
- HelpText<"Runs lldb in REPL mode with a stub process.">,
- Group<grp_repl>;
-def: Flag<["-"], "r">,
- Alias<repl>,
- HelpText<"Alias for --repl">,
- Group<grp_repl>;
-def repl_: Joined<["--", "-"], "repl=">,
- MetaVarName<"<flags>">,
- HelpText<"Runs lldb in REPL mode with a stub process with the given flags.">,
- Group<grp_repl>;
-def: Joined<["-"], "r=">,
- MetaVarName<"<flags>">,
- Alias<repl_>,
- HelpText<"Alias for --repl=<flags>">,
- Group<grp_repl>;
-
-def repl_language: Separate<["--", "-"], "repl-language">,
- MetaVarName<"<language>">,
- HelpText<"Chooses the language for the REPL.">,
- Group<grp_repl>;
-def: Separate<["-"], "R">,
- Alias<repl_language>,
- HelpText<"Alias for --repl-language">,
- Group<grp_repl>;
-
+def repl : Flag<["--", "-"], "repl">,
+ HelpText<"Runs lldb in REPL mode with a stub process.">,
+ Group<grp_repl>;
+def : Flag<["-"], "r">,
+ Alias<repl>,
+ HelpText<"Alias for --repl">,
+ Group<grp_repl>;
+def repl_
+ : Joined<["--", "-"], "repl=">,
+ MetaVarName<"<flags>">,
+ HelpText<
+ "Runs lldb in REPL mode with a stub process with the given flags.">,
+ Group<grp_repl>;
+def : Joined<["-"], "r=">,
+ MetaVarName<"<flags>">,
+ Alias<repl_>,
+ HelpText<"Alias for --repl=<flags>">,
+ Group<grp_repl>;
+
+def repl_language : Separate<["--", "-"], "repl-language">,
+ MetaVarName<"<language>">,
+ HelpText<"Chooses the language for the REPL.">,
+ Group<grp_repl>;
+def : Separate<["-"], "R">,
+ Alias<repl_language>,
+ HelpText<"Alias for --repl-language">,
+ Group<grp_repl>;
// Command options.
def grp_command : OptionGroup<"command">, HelpText<"COMMANDS">;
-def no_lldbinit: F<"no-lldbinit">,
- HelpText<"Do not automatically parse any '.lldbinit' files.">,
- Group<grp_command>;
-def: Flag<["-"], "x">,
- Alias<no_lldbinit>,
- HelpText<"Alias for --no-lldbinit">,
- Group<grp_command>;
-def local_lldbinit: F<"local-lldbinit">,
- HelpText<"Allow the debugger to parse the .lldbinit files in the current working directory, unless --no-lldbinit is passed.">,
- Group<grp_command>;
-
-def batch: F<"batch">,
- HelpText<"Tells the debugger to run the commands from -s, -S, -o & -O, and then quit.">,
- Group<grp_command>;
-def: Flag<["-"], "b">,
- Alias<batch>,
- HelpText<"Alias for --batch">,
- Group<grp_command>;
-
-def source_quietly: F<"source-quietly">,
- HelpText<"Tells the debugger not to echo commands while sourcing files or one-line commands provided on the command line.">,
- Group<grp_command>;
-def: Flag<["-"], "Q">,
- Alias<source_quietly>,
- HelpText<"Alias for --source-quietly">,
- Group<grp_command>;
-
-def one_line_on_crash: Separate<["--", "-"], "one-line-on-crash">,
- MetaVarName<"<command>">,
- HelpText<"When in batch mode, tells the debugger to run this one-line lldb command if the target crashes.">,
- Group<grp_command>;
-def: Separate<["-"], "k">,
- Alias<one_line_on_crash>,
- HelpText<"Alias for --one-line-on-crash">,
- Group<grp_command>;
-
-def source_on_crash: Separate<["--", "-"], "source-on-crash">,
- MetaVarName<"<file>">,
- HelpText<"When in batch mode, tells the debugger to source this file of lldb commands if the target crashes.">,
- Group<grp_command>;
-def: Separate<["-"], "K">,
- Alias<source_on_crash>,
- HelpText<"Alias for --source-on-crash">,
- Group<grp_command>;
-
-def source: Separate<["--", "-"], "source">,
- MetaVarName<"<file>">,
- HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, after any file has been loaded.">,
- Group<grp_command>;
-def: Separate<["-"], "s">,
- Alias<source>,
- HelpText<"Alias for --source">,
- Group<grp_command>;
-
-def source_before_file: Separate<["--", "-"], "source-before-file">,
- MetaVarName<"<file>">,
- HelpText<"Tells the debugger to read in and execute the lldb commands in the given file, before any file has been loaded.">,
- Group<grp_command>;
-def: Separate<["-"], "S">,
- Alias<source_before_file>,
- HelpText<"Alias for --source-before-file">,
- Group<grp_command>;
-
-def one_line: Separate<["--", "-"], "one-line">,
- MetaVarName<"<command>">,
- HelpText<"Tells the debugger to execute this one-line lldb command after any file provided on the command line has been loaded.">,
- Group<grp_command>;
-def: Separate<["-"], "o">,
- Alias<one_line>,
- HelpText<"Alias for --one-line">,
- Group<grp_command>;
-
-def one_line_before_file: Separate<["--", "-"], "one-line-before-file">,
- MetaVarName<"<command>">,
- HelpText<"Tells the debugger to execute this one-line lldb command before any file provided on the command line has been loaded.">,
- Group<grp_command>;
-def: Separate<["-"], "O">,
- Alias<one_line_before_file>,
- HelpText<"Alias for --one-line-before-file">,
- Group<grp_command>;
-
+def no_lldbinit : F<"no-lldbinit">,
+ HelpText<"Do not automatically parse any '.lldbinit' files.">,
+ Group<grp_command>;
+def : Flag<["-"], "x">,
+ Alias<no_lldbinit>,
+ HelpText<"Alias for --no-lldbinit">,
+ Group<grp_command>;
+def local_lldbinit
+ : F<"local-lldbinit">,
+ HelpText<"Allow the debugger to parse the .lldbinit files in the current "
+ "working directory, unless --no-lldbinit is passed.">,
+ Group<grp_command>;
+
+def batch : F<"batch">,
+ HelpText<"Tells the debugger to run the commands from -s, -S, -o & "
+ "-O, and then quit.">,
+ Group<grp_command>;
+def : Flag<["-"], "b">,
+ Alias<batch>,
+ HelpText<"Alias for --batch">,
+ Group<grp_command>;
+
+def source_quietly
+ : F<"source-quietly">,
+ HelpText<"Tells the debugger not to echo commands while sourcing files "
+ "or one-line commands provided on the command line.">,
+ Group<grp_command>;
+def : Flag<["-"], "Q">,
+ Alias<source_quietly>,
+ HelpText<"Alias for --source-quietly">,
+ Group<grp_command>;
+
+def one_line_on_crash
+ : Separate<["--", "-"], "one-line-on-crash">,
+ MetaVarName<"<command>">,
+ HelpText<"When in batch mode, tells the debugger to run this one-line "
+ "lldb command if the target crashes.">,
+ Group<grp_command>;
+def : Separate<["-"], "k">,
+ Alias<one_line_on_crash>,
+ HelpText<"Alias for --one-line-on-crash">,
+ Group<grp_command>;
+
+def source_on_crash
+ : Separate<["--", "-"], "source-on-crash">,
+ MetaVarName<"<file>">,
+ HelpText<"When in batch mode, tells the debugger to source this file of "
+ "lldb commands if the target crashes.">,
+ Group<grp_command>;
+def : Separate<["-"], "K">,
+ Alias<source_on_crash>,
+ HelpText<"Alias for --source-on-crash">,
+ Group<grp_command>;
+
+def source
+ : Separate<["--", "-"], "source">,
+ MetaVarName<"<file>">,
+ HelpText<"Tells the debugger to read in and execute the lldb commands in "
+ "the given file, after any file has been loaded.">,
+ Group<grp_command>;
+def : Separate<["-"], "s">,
+ Alias<source>,
+ HelpText<"Alias for --source">,
+ Group<grp_command>;
+
+def source_before_file
+ : Separate<["--", "-"], "source-before-file">,
+ MetaVarName<"<file>">,
+ HelpText<"Tells the debugger to read in and execute the lldb commands in "
+ "the given file, before any file has been loaded.">,
+ Group<grp_command>;
+def : Separate<["-"], "S">,
+ Alias<source_before_file>,
+ HelpText<"Alias for --source-before-file">,
+ Group<grp_command>;
+
+def one_line
+ : Separate<["--", "-"], "one-line">,
+ MetaVarName<"<command>">,
+ HelpText<"Tells the debugger to execute this one-line lldb command after "
+ "any file provided on the command line has been loaded.">,
+ Group<grp_command>;
+def : Separate<["-"], "o">,
+ Alias<one_line>,
+ HelpText<"Alias for --one-line">,
+ Group<grp_command>;
+
+def one_line_before_file
+ : Separate<["--", "-"], "one-line-before-file">,
+ MetaVarName<"<command>">,
+ HelpText<"Tells the debugger to execute this one-line lldb command "
+ "before any file provided on the command line has been loaded.">,
+ Group<grp_command>;
+def : Separate<["-"], "O">,
+ Alias<one_line_before_file>,
+ HelpText<"Alias for --one-line-before-file">,
+ Group<grp_command>;
// General options.
-def version: F<"version">,
- HelpText<"Prints out the current version number of the LLDB debugger.">;
-def: Flag<["-"], "v">,
- Alias<version>,
- HelpText<"Alias for --version">;
-
-def help: F<"help">,
- HelpText<"Prints out the usage information for the LLDB debugger.">;
-def: Flag<["-"], "h">,
- Alias<help>,
- HelpText<"Alias for --help">;
-
-def core: Separate<["--", "-"], "core">,
- MetaVarName<"<filename>">,
- HelpText<"Tells the debugger to use the full path to <filename> as the core file.">;
-def: Separate<["-"], "c">,
- Alias<core>,
- HelpText<"Alias for --core">;
-
-def editor: F<"editor">,
- HelpText<"Tells the debugger to open source files using the host's \"external editor\" mechanism.">;
-def: Flag<["-"], "e">,
- Alias<editor>,
- HelpText<"Alias for --editor">;
-
-def no_use_colors: F<"no-use-colors">,
- HelpText<"Do not use colors.">;
-def: Flag<["-"], "X">,
- Alias<no_use_colors>,
- HelpText<"Alias for --no-use-color">;
-
-def file: Separate<["--", "-"], "file">,
- MetaVarName<"<filename>">,
- HelpText<"Tells the debugger to use the file <filename> as the program to be debugged.">;
-def: Separate<["-"], "f">,
- Alias<file>,
- HelpText<"Alias for --file">;
-
-def arch: Separate<["--", "-"], "arch">,
- MetaVarName<"<architecture>">,
- HelpText<"Tells the debugger to use the specified architecture when starting and running the program.">;
-def: Separate<["-"], "a">,
- Alias<arch>,
- HelpText<"Alias for --arch">;
-
-def debug: F<"debug">,
- HelpText<"Tells the debugger to print out extra information for debugging itself.">;
-def: Flag<["-"], "d">,
- Alias<debug>,
- HelpText<"Alias for --debug">;
+def version
+ : F<"version">,
+ HelpText<"Prints out the current version number of the LLDB debugger.">;
+def : Flag<["-"], "v">, Alias<version>, HelpText<"Alias for --version">;
+
+def help : F<"help">,
+ HelpText<"Prints out the usage information for the LLDB debugger.">;
+def : Flag<["-"], "h">, Alias<help>, HelpText<"Alias for --help">;
+
+def core : Separate<["--", "-"], "core">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to use the full path to <filename> as "
+ "the core file.">;
+def : Separate<["-"], "c">, Alias<core>, HelpText<"Alias for --core">;
+
+def editor : F<"editor">,
+ HelpText<"Tells the debugger to open source files using the "
+ "host's \"external editor\" mechanism.">;
+def : Flag<["-"], "e">, Alias<editor>, HelpText<"Alias for --editor">;
+
+def no_use_colors : F<"no-use-colors">, HelpText<"Do not use colors.">;
+def : Flag<["-"], "X">,
+ Alias<no_use_colors>,
+ HelpText<"Alias for --no-use-colors">;
+
+def file : Separate<["--", "-"], "file">,
+ MetaVarName<"<filename>">,
+ HelpText<"Tells the debugger to use the file <filename> as the "
+ "program to be debugged.">;
+def : Separate<["-"], "f">, Alias<file>, HelpText<"Alias for --file">;
+
+def arch : Separate<["--", "-"], "arch">,
+ MetaVarName<"<architecture>">,
+ HelpText<"Tells the debugger to use the specified architecture when "
+ "starting and running the program.">;
+def : Separate<["-"], "a">, Alias<arch>, HelpText<"Alias for --arch">;
+
+def debug : F<"debug">,
+ HelpText<"Tells the debugger to print out extra information for "
+ "debugging itself.">;
+def : Flag<["-"], "d">, Alias<debug>, HelpText<"Alias for --debug">;
def REM : R<["--"], "">;
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index b1ad38d98389..f76656e98ca0 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -121,12 +121,13 @@ static std::string capitalize(llvm::StringRef str) {
llvm::StringRef DAP::debug_adapter_path = "";
DAP::DAP(Log *log, const ReplMode default_repl_mode,
- std::vector<std::string> pre_init_commands,
+ std::vector<std::string> pre_init_commands, bool no_lldbinit,
llvm::StringRef client_name, DAPTransport &transport, MainLoop &loop)
: log(log), transport(transport), broadcaster("lldb-dap"),
progress_event_reporter(
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
- repl_mode(default_repl_mode), m_client_name(client_name), m_loop(loop) {
+ repl_mode(default_repl_mode), no_lldbinit(no_lldbinit),
+ m_client_name(client_name), m_loop(loop) {
configuration.preInitCommands = std::move(pre_init_commands);
RegisterRequests();
}
@@ -1068,6 +1069,11 @@ llvm::Error DAP::Loop() {
out.Stop();
err.Stop();
StopEventHandlers();
+
+ // Destroy the debugger when the session ends. This will trigger the
+ // debugger's destroy callbacks for earlier logging and clean-ups, rather
+ // than waiting for the termination of the lldb-dap process.
+ lldb::SBDebugger::Destroy(debugger);
});
while (true) {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 04f70f76a09c..71681fd4b51e 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -156,6 +156,9 @@ struct DAP final : private DAPTransport::MessageHandler {
/// The set of features supported by the connected client.
llvm::DenseSet<ClientFeature> clientFeatures;
+ /// Whether to disable sourcing .lldbinit files.
+ bool no_lldbinit;
+
/// The initial thread list upon attaching.
std::vector<protocol::Thread> initial_thread_list;
@@ -178,13 +181,16 @@ struct DAP final : private DAPTransport::MessageHandler {
/// \param[in] pre_init_commands
/// LLDB commands to execute as soon as the debugger instance is
/// allocated.
+ /// \param[in] no_lldbinit
+ /// Whether to disable sourcing .lldbinit files.
/// \param[in] transport
/// Transport for this debug session.
/// \param[in] loop
/// Main loop associated with this instance.
DAP(Log *log, const ReplMode default_repl_mode,
- std::vector<std::string> pre_init_commands, llvm::StringRef client_name,
- DAPTransport &transport, lldb_private::MainLoop &loop);
+ std::vector<std::string> pre_init_commands, bool no_lldbinit,
+ llvm::StringRef client_name, DAPTransport &transport,
+ lldb_private::MainLoop &loop);
~DAP();
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index bfb05a387d04..ecd630cb530d 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -230,15 +230,7 @@ llvm::Error SendThreadStoppedEvent(DAP &dap, bool on_entry) {
// Send a "terminated" event to indicate the process is done being
// debugged.
-void SendTerminatedEvent(DAP &dap) {
- // Prevent races if the process exits while we're being asked to disconnect.
- llvm::call_once(dap.terminated_event_flag, [&] {
- dap.RunTerminateCommands();
- // Send a "terminated" event
- llvm::json::Object event(CreateTerminatedEventObject(dap.target));
- dap.SendJSON(llvm::json::Value(std::move(event)));
- });
-}
+void SendTerminatedEvent(DAP &dap) { dap.SendTerminatedEvent(); }
// Grab any STDOUT and STDERR from the process and send it up to VS Code
// via an "output" event to the "stdout" and "stderr" categories.
diff --git a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
index b499a69876e2..9069de4a3a69 100644
--- a/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/InitializeRequestHandler.cpp
@@ -42,8 +42,11 @@ llvm::Expected<InitializeResponse> InitializeRequestHandler::Run(
// The sourceInitFile option is not part of the DAP specification. It is an
// extension used by the test suite to prevent sourcing `.lldbinit` and
- // changing its behavior.
- if (arguments.lldbExtSourceInitFile.value_or(true)) {
+ // changing its behavior. The CLI flag --no-lldbinit takes precedence over
+ // the DAP parameter.
+ bool should_source_init_files =
+ !dap.no_lldbinit && arguments.lldbExtSourceInitFile.value_or(true);
+ if (should_source_init_files) {
dap.debugger.SkipLLDBInitFiles(false);
dap.debugger.SkipAppInitFiles(false);
lldb::SBCommandReturnObject init;
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index 867753e9294a..c8492c6a62b2 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -61,3 +61,18 @@ def pre_init_command: S<"pre-init-command">,
def: Separate<["-"], "c">,
Alias<pre_init_command>,
HelpText<"Alias for --pre-init-command">;
+
+def no_lldbinit: F<"no-lldbinit">,
+ HelpText<"Do not automatically parse any '.lldbinit' files.">;
+def: Flag<["-"], "x">,
+ Alias<no_lldbinit>,
+ HelpText<"Alias for --no-lldbinit">;
+
+def connection_timeout: S<"connection-timeout">,
+ MetaVarName<"<timeout>">,
+ HelpText<"When using --connection, the number of seconds to wait for new "
+ "connections after the server has started and after all clients have "
+ "disconnected. Each new connection will reset the timeout. When the "
+ "timeout is reached, the server will be closed and the process will exit. "
+ "Not specifying this argument or specifying non-positive values will "
+ "cause the server to wait for new connections indefinitely.">;
diff --git a/lldb/tools/lldb-dap/Protocol/DAPTypes.h b/lldb/tools/lldb-dap/Protocol/DAPTypes.h
index 7fccf1359a73..23ba93946bde 100644
--- a/lldb/tools/lldb-dap/Protocol/DAPTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/DAPTypes.h
@@ -16,6 +16,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
+#include "lldb/lldb-defines.h"
#include "lldb/lldb-types.h"
#include "llvm/Support/JSON.h"
#include <optional>
@@ -50,29 +51,29 @@ llvm::json::Value toJSON(const SourceLLDBData &);
struct Symbol {
/// The symbol id, usually the original symbol table index.
- uint32_t id;
+ uint32_t id = 0;
/// True if this symbol is debug information in a symbol.
- bool isDebug;
+ bool isDebug = false;
/// True if this symbol is not actually in the symbol table, but synthesized
/// from other info in the object file.
- bool isSynthetic;
+ bool isSynthetic = false;
/// True if this symbol is globally visible.
- bool isExternal;
+ bool isExternal = false;
/// The symbol type.
- lldb::SymbolType type;
+ lldb::SymbolType type = lldb::eSymbolTypeInvalid;
/// The symbol file address.
- lldb::addr_t fileAddress;
+ lldb::addr_t fileAddress = LLDB_INVALID_ADDRESS;
/// The symbol load address.
std::optional<lldb::addr_t> loadAddress;
/// The symbol size.
- lldb::addr_t size;
+ lldb::addr_t size = 0;
/// The symbol name.
std::string name;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index eab7211e1897..e1806d6230a8 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -219,7 +219,7 @@ bool fromJSON(const json::Value &Params, InitializeRequestArguments &IRA,
OM.map("clientName", IRA.clientName) && OM.map("locale", IRA.locale) &&
OM.map("linesStartAt1", IRA.linesStartAt1) &&
OM.map("columnsStartAt1", IRA.columnsStartAt1) &&
- OM.map("pathFormat", IRA.pathFormat) &&
+ OM.mapOptional("pathFormat", IRA.pathFormat) &&
OM.map("$__lldb_sourceInitFile", IRA.lldbExtSourceInitFile);
}
diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md
index f88f3ced6f25..39dabcc1342c 100644
--- a/lldb/tools/lldb-dap/README.md
+++ b/lldb/tools/lldb-dap/README.md
@@ -275,9 +275,9 @@ User settings can set the default value for the following supported
| **exitCommands** | [string] | `[]` |
| **terminateCommands** | [string] | `[]` |
-To adjust your settings, open the Settings editor via the
-`File > Preferences > Settings` menu or press `Ctrl+`, on Windows/Linux and
-`Cmd+`, on Mac.
+To adjust your settings, open the Settings editor
+via the `File > Preferences > Settings` menu or press `Ctrl+,` on Windows/Linux,
+and the `VS Code > Settings... > Settings` menu or press `Cmd+,` on Mac.
## Debug Console
@@ -372,6 +372,19 @@ for more details on Debug Adapter Protocol events and the VS Code
[debug.onDidReceiveDebugSessionCustomEvent](https://code.visualstudio.com/api/references/vscode-api#debug.onDidReceiveDebugSessionCustomEvent)
API for handling a custom event from an extension.
+## Server Mode
+
+lldb-dap supports a server mode that can be enabled via the following user settings.
+
+| Setting | Type | Default | |
+| -------------------------- | -------- | :-----: | --------- |
+| **Server Mode** | string | `False` | Run lldb-dap in server mode. When enabled, lldb-dap will start a background server that will be reused between debug sessions. This allows caching of debug symbols between sessions and improves launch performance.
+| **Connection Timeout** | number | `0` | When running lldb-dap in server mode, the time in seconds to wait for new connections after the server has started and after all clients have disconnected. Each new connection will reset the timeout. When the timeout is reached, the server will be closed and the process will exit. Specifying non-positive values will cause the server to wait for new connections indefinitely.
+
+To adjust your settings, open the Settings editor
+via the `File > Preferences > Settings` menu or press `Ctrl+,` on Windows/Linux,
+and the `VS Code > Settings... > Settings` menu or press `Cmd+,` on Mac.
+
## Contributing
`lldb-dap` and `lldb` are developed under the umbrella of the [LLVM project](https://llvm.org/).
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index f11b64aa72ae..9cc653cee405 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -106,6 +106,13 @@
"markdownDescription": "Run lldb-dap in server mode.\n\nWhen enabled, lldb-dap will start a background server that will be reused between debug sessions. This allows caching of debug symbols between sessions and improves launch performance.",
"default": false
},
+ "lldb-dap.connectionTimeout": {
+ "order": 0,
+ "scope": "resource",
+ "type": "number",
+ "markdownDescription": "When running lldb-dap in server mode, the time in seconds to wait for new connections after the server has started and after all clients have disconnected. Each new connection will reset the timeout. When the timeout is reached, the server will be closed and the process will exit. Specifying non-positive values will cause the server to wait for new connections indefinitely.",
+ "default": 0
+ },
"lldb-dap.arguments": {
"scope": "resource",
"type": "array",
@@ -398,6 +405,29 @@
},
"markdownDescription": "The list of additional arguments used to launch the debug adapter executable. Overrides any user or workspace settings."
},
+ "debugAdapterEnv": {
+ "anyOf": [
+ {
+ "type": "object",
+ "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `{ \"FOO\": \"1\" }`",
+ "patternProperties": {
+ ".*": {
+ "type": "string"
+ }
+ },
+ "default": {}
+ },
+ {
+ "type": "array",
+ "markdownDescription": "Additional environment variables to set when launching the debug adapter executable. E.g. `[\"FOO=1\", \"BAR\"]`",
+ "items": {
+ "type": "string",
+ "pattern": "^((\\w+=.*)|^\\w+)$"
+ },
+ "default": []
+ }
+ ]
+ },
"program": {
"type": "string",
"description": "Path to the program to debug."
diff --git a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
index 157aa2ac76a1..f7e92ee95ca3 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts
@@ -69,6 +69,40 @@ async function findDAPExecutable(): Promise<string | undefined> {
}
/**
+ * Validates the DAP environment provided in the debug configuration.
+ * It must be a dictionary of string keys and values OR an array of string values.
+ *
+ * @param debugConfigEnv The supposed DAP environment that will be validated
+ * @returns Whether or not the DAP environment is valid
+ */
+function validateDAPEnv(debugConfigEnv: any): boolean {
+ // If the env is an object, it should have string values.
+ // The keys are guaranteed to be strings.
+ if (
+ typeof debugConfigEnv === "object" &&
+ Object.values(debugConfigEnv).findIndex(
+ (entry) => typeof entry !== "string",
+ ) !== -1
+ ) {
+ return false;
+ }
+
+ // If the env is an array, it should have string values which match the regex.
+ if (
+ Array.isArray(debugConfigEnv) &&
+ debugConfigEnv.findIndex(
+ (entry) =>
+ typeof entry !== "string" || !/^((\\w+=.*)|^\\w+)$/.test(entry),
+ ) !== -1
+ ) {
+ return false;
+ }
+
+ // The env is valid.
+ return true;
+}
+
+/**
* Retrieves the lldb-dap executable path either from settings or the provided
* {@link vscode.DebugConfiguration}.
*
@@ -158,6 +192,51 @@ async function getDAPArguments(
}
/**
+ * Retrieves the environment that will be provided to lldb-dap either from settings or the provided
+ * {@link vscode.DebugConfiguration}.
+ *
+ * @param workspaceFolder The {@link vscode.WorkspaceFolder} that the debug session will be launched within
+ * @param configuration The {@link vscode.DebugConfiguration} that will be launched
+ * @throws An {@link ErrorWithNotification} if something went wrong
+ * @returns The environment that will be provided to lldb-dap
+ */
+async function getDAPEnvironment(
+ workspaceFolder: vscode.WorkspaceFolder | undefined,
+ configuration: vscode.DebugConfiguration,
+): Promise<{ [key: string]: string }> {
+ const debugConfigEnv = configuration.debugAdapterEnv;
+ if (debugConfigEnv) {
+ if (validateDAPEnv(debugConfigEnv) === false) {
+ throw new ErrorWithNotification(
+ "The debugAdapterEnv property must be a dictionary of string keys and values OR an array of string values. Please update your launch configuration",
+ new ConfigureButton(),
+ );
+ }
+
+ // Transform, so that the returned value is always a dictionary.
+ if (Array.isArray(debugConfigEnv)) {
+ const ret: { [key: string]: string } = {};
+ for (const envVar of debugConfigEnv as string[]) {
+ const equalSignPos = envVar.search("=");
+ if (equalSignPos >= 0) {
+ ret[envVar.substr(0, equalSignPos)] = envVar.substr(equalSignPos + 1);
+ } else {
+ ret[envVar] = "";
+ }
+ }
+ return ret;
+ } else {
+ return debugConfigEnv;
+ }
+ }
+
+ const config = vscode.workspace.workspaceFile
+ ? vscode.workspace.getConfiguration("lldb-dap")
+ : vscode.workspace.getConfiguration("lldb-dap", workspaceFolder);
+ return config.get<{ [key: string]: string }>("environment") || {};
+}
+
+/**
* Creates a new {@link vscode.DebugAdapterExecutable} based on the provided workspace folder and
* debug configuration. Assumes that the given debug configuration is for a local launch of lldb-dap.
*
@@ -182,12 +261,16 @@ export async function createDebugAdapterExecutable(
if (log_path) {
env["LLDBDAP_LOG"] = log_path;
} else if (
- vscode.workspace.getConfiguration("lldb-dap").get("captureSessionLogs", false)
+ vscode.workspace
+ .getConfiguration("lldb-dap")
+ .get("captureSessionLogs", false)
) {
env["LLDBDAP_LOG"] = logFilePath.get(LogType.DEBUG_SESSION);
}
- const configEnvironment =
- config.get<{ [key: string]: string }>("environment") || {};
+ const configEnvironment = await getDAPEnvironment(
+ workspaceFolder,
+ configuration,
+ );
const dapPath = await getDAPExecutable(workspaceFolder, configuration);
const dbgOptions = {
diff --git a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
index 1ae87116141f..d35460ab68f0 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
@@ -207,10 +207,15 @@ export class LLDBDapConfigurationProvider
config.get<boolean>("serverMode", false) &&
(await isServerModeSupported(executable.command))
) {
+ const connectionTimeoutSeconds = config.get<number | undefined>(
+ "connectionTimeout",
+ undefined,
+ );
const serverInfo = await this.server.start(
executable.command,
executable.args,
executable.options,
+ connectionTimeoutSeconds,
);
if (!serverInfo) {
return undefined;
diff --git a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
index 5f9d8efdcb3a..774be50053a1 100644
--- a/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
+++ b/lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts
@@ -11,6 +11,7 @@ import * as vscode from "vscode";
export class LLDBDapServer implements vscode.Disposable {
private serverProcess?: child_process.ChildProcessWithoutNullStreams;
private serverInfo?: Promise<{ host: string; port: number }>;
+ private serverSpawnInfo?: string[];
constructor() {
vscode.commands.registerCommand(
@@ -32,9 +33,20 @@ export class LLDBDapServer implements vscode.Disposable {
dapPath: string,
args: string[],
options?: child_process.SpawnOptionsWithoutStdio,
+ connectionTimeoutSeconds?: number,
): Promise<{ host: string; port: number } | undefined> {
- const dapArgs = [...args, "--connection", "listen://localhost:0"];
- if (!(await this.shouldContinueStartup(dapPath, dapArgs))) {
+ // Both the --connection and --connection-timeout arguments are subject to the shouldContinueStartup() check.
+ const connectionTimeoutArgs =
+ connectionTimeoutSeconds && connectionTimeoutSeconds > 0
+ ? ["--connection-timeout", `${connectionTimeoutSeconds}`]
+ : [];
+ const dapArgs = [
+ ...args,
+ "--connection",
+ "listen://localhost:0",
+ ...connectionTimeoutArgs,
+ ];
+ if (!(await this.shouldContinueStartup(dapPath, dapArgs, options?.env))) {
return undefined;
}
@@ -70,6 +82,7 @@ export class LLDBDapServer implements vscode.Disposable {
}
});
this.serverProcess = process;
+ this.serverSpawnInfo = this.getSpawnInfo(dapPath, dapArgs, options?.env);
});
return this.serverInfo;
}
@@ -85,12 +98,14 @@ export class LLDBDapServer implements vscode.Disposable {
private async shouldContinueStartup(
dapPath: string,
args: string[],
+ env: NodeJS.ProcessEnv | { [key: string]: string } | undefined,
): Promise<boolean> {
- if (!this.serverProcess || !this.serverInfo) {
+ if (!this.serverProcess || !this.serverInfo || !this.serverSpawnInfo) {
return true;
}
- if (isDeepStrictEqual(this.serverProcess.spawnargs, [dapPath, ...args])) {
+ const newSpawnInfo = this.getSpawnInfo(dapPath, args, env);
+ if (isDeepStrictEqual(this.serverSpawnInfo, newSpawnInfo)) {
return true;
}
@@ -102,11 +117,11 @@ export class LLDBDapServer implements vscode.Disposable {
The previous lldb-dap server was started with:
-${this.serverProcess.spawnargs.join(" ")}
+${this.serverSpawnInfo.join(" ")}
The new lldb-dap server will be started with:
-${dapPath} ${args.join(" ")}
+${newSpawnInfo.join(" ")}
Restarting the server will interrupt any existing debug sessions and start a new server.`,
},
@@ -143,4 +158,18 @@ Restarting the server will interrupt any existing debug sessions and start a new
this.serverInfo = undefined;
}
}
+
+ getSpawnInfo(
+ path: string,
+ args: string[],
+ env: NodeJS.ProcessEnv | { [key: string]: string } | undefined,
+ ): string[] {
+ return [
+ path,
+ ...args,
+ ...Object.entries(env ?? {}).map(
+ (entry) => String(entry[0]) + "=" + String(entry[1]),
+ ),
+ ];
+ }
}
diff --git a/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts
index 84b9387ffe49..951a5971e0bc 100644
--- a/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts
@@ -61,18 +61,18 @@ export class SymbolsProvider extends DisposableContext {
return;
}
- this.showSymbolsForModule(session, selectedModule.module);
+ await this.showSymbolsForModule(session, selectedModule.module);
}
private async showSymbolsForModule(session: vscode.DebugSession, module: DebugProtocol.Module) {
try {
const symbols = await this.getSymbolsForModule(session, module.id.toString());
- this.showSymbolsInNewTab(module.name.toString(), symbols);
+ await this.showSymbolsInNewTab(module.name.toString(), symbols);
} catch (error) {
if (error instanceof Error) {
- vscode.window.showErrorMessage("Failed to retrieve symbols: " + error.message);
+ await vscode.window.showErrorMessage("Failed to retrieve symbols: " + error.message);
} else {
- vscode.window.showErrorMessage("Failed to retrieve symbols due to an unknown error.");
+ await vscode.window.showErrorMessage("Failed to retrieve symbols due to an unknown error.");
}
return;
@@ -106,7 +106,7 @@ export class SymbolsProvider extends DisposableContext {
const symbolsTableScriptPath = panel.webview.asWebviewUri(vscode.Uri.joinPath(this.getExtensionResourcePath(), "symbols-table-view.js"));
panel.webview.html = getSymbolsTableHTMLContent(tabulatorJsPath, tabulatorCssPath, symbolsTableScriptPath);
- panel.webview.postMessage({ command: "updateSymbols", symbols: symbols });
+ await panel.webview.postMessage({ command: "updateSymbols", symbols: symbols });
}
private getExtensionResourcePath(): vscode.Uri {
diff --git a/lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts b/lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts
index 88e24f310878..c00e0d462569 100644
--- a/lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts
+++ b/lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts
@@ -12,8 +12,13 @@ export function getSymbolsTableHTMLContent(tabulatorJsPath: vscode.Uri, tabulato
color: var(--vscode-editor-foreground);
}
+ .tabulator .tabulator-header {
+ background-color: var(--vscode-tree-tableOddRowsBackground);
+ color: var(--vscode-editor-foreground);
+ }
+
.tabulator .tabulator-header .tabulator-col {
- background-color: var(--vscode-editor-background);
+ background-color: var(--vscode-tree-tableOddRowsBackground);
color: var(--vscode-editor-foreground);
}
@@ -23,11 +28,22 @@ export function getSymbolsTableHTMLContent(tabulatorJsPath: vscode.Uri, tabulato
}
.tabulator-row.tabulator-row-even {
+ background-color: var(--vscode-tree-tableOddRowsBackground);
+ }
+
+ @media (hover:hover) and (pointer:fine){
+ .tabulator-row:hover {
+ background-color: var(--vscode-list-hoverBackground);
+ color: var(--vscode-list-hoverForeground);
+ }
+ }
+
+ .tabulator-row.tabulator-selected {
background-color: var(--vscode-editor-background);
color: var(--vscode-editor-foreground);
}
- .tabulator-row.tabulator-selected {
+ .tabulator .tabulator-tableholder .tabulator-table {
background-color: var(--vscode-editor-background);
color: var(--vscode-editor-foreground);
}
diff --git a/lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts b/lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts
index 8454378abef1..9d346818e384 100644
--- a/lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts
+++ b/lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts
@@ -95,6 +95,7 @@ const SYMBOLS_TABLE = new Tabulator("#symbols-table", {
height: "100vh",
columns: SYMBOL_TABLE_COLUMNS,
layout: "fitColumns",
+ selectableRows: false,
data: previousState?.symbols || [],
});
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index b74085f25f4e..93446c051eb5 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -223,6 +223,35 @@ static int DuplicateFileDescriptor(int fd) {
#endif
}
+static void
+ResetConnectionTimeout(std::mutex &connection_timeout_mutex,
+ MainLoopBase::TimePoint &conncetion_timeout_time_point) {
+ std::scoped_lock<std::mutex> lock(connection_timeout_mutex);
+ conncetion_timeout_time_point = MainLoopBase::TimePoint();
+}
+
+static void
+TrackConnectionTimeout(MainLoop &loop, std::mutex &connection_timeout_mutex,
+ MainLoopBase::TimePoint &conncetion_timeout_time_point,
+ std::chrono::seconds ttl_seconds) {
+ MainLoopBase::TimePoint next_checkpoint =
+ std::chrono::steady_clock::now() + std::chrono::seconds(ttl_seconds);
+ {
+ std::scoped_lock<std::mutex> lock(connection_timeout_mutex);
+ // We don't need to take the max of `ttl_time_point` and `next_checkpoint`,
+ // because `next_checkpoint` must be the latest.
+ conncetion_timeout_time_point = next_checkpoint;
+ }
+ loop.AddCallback(
+ [&connection_timeout_mutex, &conncetion_timeout_time_point,
+ next_checkpoint](MainLoopBase &loop) {
+ std::scoped_lock<std::mutex> lock(connection_timeout_mutex);
+ if (conncetion_timeout_time_point == next_checkpoint)
+ loop.RequestTermination();
+ },
+ next_checkpoint);
+}
+
static llvm::Expected<std::pair<Socket::SocketProtocol, std::string>>
validateConnection(llvm::StringRef conn) {
auto uri = lldb_private::URI::Parse(conn);
@@ -255,10 +284,11 @@ validateConnection(llvm::StringRef conn) {
return make_error();
}
-static llvm::Error
-serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
- Log *log, const ReplMode default_repl_mode,
- const std::vector<std::string> &pre_init_commands) {
+static llvm::Error serveConnection(
+ const Socket::SocketProtocol &protocol, const std::string &name, Log *log,
+ const ReplMode default_repl_mode,
+ const std::vector<std::string> &pre_init_commands, bool no_lldbinit,
+ std::optional<std::chrono::seconds> connection_timeout_seconds) {
Status status;
static std::unique_ptr<Socket> listener = Socket::Create(protocol, status);
if (status.Fail()) {
@@ -283,6 +313,12 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
g_loop.AddPendingCallback(
[](MainLoopBase &loop) { loop.RequestTermination(); });
});
+ static MainLoopBase::TimePoint g_connection_timeout_time_point;
+ static std::mutex g_connection_timeout_mutex;
+ if (connection_timeout_seconds)
+ TrackConnectionTimeout(g_loop, g_connection_timeout_mutex,
+ g_connection_timeout_time_point,
+ connection_timeout_seconds.value());
std::condition_variable dap_sessions_condition;
std::mutex dap_sessions_mutex;
std::map<MainLoop *, DAP *> dap_sessions;
@@ -291,6 +327,11 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
&dap_sessions_mutex, &dap_sessions,
&clientCount](
std::unique_ptr<Socket> sock) {
+ // Reset the keep alive timer, because we won't be killing the server
+ // while this connection is being served.
+ if (connection_timeout_seconds)
+ ResetConnectionTimeout(g_connection_timeout_mutex,
+ g_connection_timeout_time_point);
std::string client_name = llvm::formatv("client_{0}", clientCount++).str();
DAP_LOG(log, "({0}) client connected", client_name);
@@ -303,8 +344,8 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
llvm::set_thread_name(client_name + ".runloop");
MainLoop loop;
Transport transport(client_name, log, io, io);
- DAP dap(log, default_repl_mode, pre_init_commands, client_name, transport,
- loop);
+ DAP dap(log, default_repl_mode, pre_init_commands, no_lldbinit,
+ client_name, transport, loop);
if (auto Err = dap.ConfigureIO()) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
@@ -327,6 +368,12 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
std::unique_lock<std::mutex> lock(dap_sessions_mutex);
dap_sessions.erase(&loop);
std::notify_all_at_thread_exit(dap_sessions_condition, std::move(lock));
+
+ // Start the countdown to kill the server at the end of each connection.
+ if (connection_timeout_seconds)
+ TrackConnectionTimeout(g_loop, g_connection_timeout_mutex,
+ g_connection_timeout_time_point,
+ connection_timeout_seconds.value());
});
client.detach();
});
@@ -456,6 +503,31 @@ int main(int argc, char *argv[]) {
connection.assign(path);
}
+ std::optional<std::chrono::seconds> connection_timeout_seconds;
+ if (llvm::opt::Arg *connection_timeout_arg =
+ input_args.getLastArg(OPT_connection_timeout)) {
+ if (!connection.empty()) {
+ llvm::StringRef connection_timeout_string_value =
+ connection_timeout_arg->getValue();
+ int connection_timeout_int_value;
+ if (connection_timeout_string_value.getAsInteger(
+ 10, connection_timeout_int_value)) {
+ llvm::errs() << "'" << connection_timeout_string_value
+ << "' is not a valid connection timeout value\n";
+ return EXIT_FAILURE;
+ }
+ // Ignore non-positive values.
+ if (connection_timeout_int_value > 0)
+ connection_timeout_seconds =
+ std::chrono::seconds(connection_timeout_int_value);
+ } else {
+ llvm::errs()
+ << "\"--connection-timeout\" requires \"--connection\" to be "
+ "specified\n";
+ return EXIT_FAILURE;
+ }
+ }
+
#if !defined(_WIN32)
if (input_args.hasArg(OPT_wait_for_debugger)) {
printf("Paused waiting for debugger to attach (pid = %i)...\n", getpid());
@@ -508,6 +580,8 @@ int main(int argc, char *argv[]) {
pre_init_commands.push_back(arg);
}
+ bool no_lldbinit = input_args.hasArg(OPT_no_lldbinit);
+
if (!connection.empty()) {
auto maybeProtoclAndName = validateConnection(connection);
if (auto Err = maybeProtoclAndName.takeError()) {
@@ -520,7 +594,8 @@ int main(int argc, char *argv[]) {
std::string name;
std::tie(protocol, name) = *maybeProtoclAndName;
if (auto Err = serveConnection(protocol, name, log.get(), default_repl_mode,
- pre_init_commands)) {
+ pre_init_commands, no_lldbinit,
+ connection_timeout_seconds)) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
"Connection failed: ");
return EXIT_FAILURE;
@@ -556,8 +631,8 @@ int main(int argc, char *argv[]) {
constexpr llvm::StringLiteral client_name = "stdio";
MainLoop loop;
Transport transport(client_name, log.get(), input, output);
- DAP dap(log.get(), default_repl_mode, pre_init_commands, client_name,
- transport, loop);
+ DAP dap(log.get(), default_repl_mode, pre_init_commands, no_lldbinit,
+ client_name, transport, loop);
// stdout/stderr redirection to the IDE's console
if (auto Err = dap.ConfigureIO(stdout, stderr)) {
diff --git a/lldb/tools/lldb-mcp/CMakeLists.txt b/lldb/tools/lldb-mcp/CMakeLists.txt
new file mode 100644
index 000000000000..5f61a1993cea
--- /dev/null
+++ b/lldb/tools/lldb-mcp/CMakeLists.txt
@@ -0,0 +1,34 @@
+add_lldb_tool(lldb-mcp
+ lldb-mcp.cpp
+
+ LINK_COMPONENTS
+ Option
+ Support
+ LINK_LIBS
+ liblldb
+ lldbInitialization
+ lldbHost
+ lldbProtocolMCP
+ )
+
+if(APPLE)
+ configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lldb-mcp-Info.plist.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lldb-mcp-Info.plist
+ )
+ target_link_options(lldb-mcp
+ PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-mcp-Info.plist)
+endif()
+
+if(LLDB_BUILD_FRAMEWORK)
+ # In the build-tree, we know the exact path to the framework directory.
+ # The installed framework can be in different locations.
+ lldb_setup_rpaths(lldb-mcp
+ BUILD_RPATH
+ "${LLDB_FRAMEWORK_ABSOLUTE_BUILD_DIR}"
+ INSTALL_RPATH
+ "@loader_path/../../../SharedFrameworks"
+ "@loader_path/../../System/Library/PrivateFrameworks"
+ "@loader_path/../../Library/PrivateFrameworks"
+ )
+endif()
diff --git a/lldb/tools/lldb-mcp/lldb-mcp-Info.plist.in b/lldb/tools/lldb-mcp/lldb-mcp-Info.plist.in
new file mode 100644
index 000000000000..4dc3ddd91280
--- /dev/null
+++ b/lldb/tools/lldb-mcp/lldb-mcp-Info.plist.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.lldb-mcp</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>lldb-mcp</string>
+ <key>CFBundleVersion</key>
+ <string>${LLDB_VERSION}</string>
+ <key>SecTaskAccess</key>
+ <array>
+ <string>allowed</string>
+ <string>debug</string>
+ </array>
+</dict>
+</plist>
diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
new file mode 100644
index 000000000000..12545dcf3a3c
--- /dev/null
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -0,0 +1,176 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.h"
+#include "lldb/Host/File.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Initialization/SystemInitializerCommon.h"
+#include "lldb/Initialization/SystemLifetimeManager.h"
+#include "lldb/Protocol/MCP/Server.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/UriParser.h"
+#include "lldb/lldb-forward.h"
+#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+#include <cstdlib>
+#include <memory>
+
+#if defined(_WIN32)
+#include <fcntl.h>
+#endif
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_protocol::mcp;
+
+using lldb_private::File;
+using lldb_private::MainLoop;
+using lldb_private::MainLoopBase;
+using lldb_private::NativeFile;
+
+namespace {
+
+inline void exitWithError(llvm::Error Err, StringRef Prefix = "") {
+ handleAllErrors(std::move(Err), [&](ErrorInfoBase &Info) {
+ WithColor::error(errs(), Prefix) << Info.message() << '\n';
+ });
+ std::exit(EXIT_FAILURE);
+}
+
+constexpr size_t kForwardIOBufferSize = 1024;
+
+void forwardIO(lldb_private::MainLoopBase &loop, lldb::IOObjectSP &from,
+ lldb::IOObjectSP &to) {
+ char buf[kForwardIOBufferSize];
+ size_t num_bytes = sizeof(buf);
+
+ if (llvm::Error err = from->Read(buf, num_bytes).takeError())
+ exitWithError(std::move(err));
+
+ // EOF reached.
+ if (num_bytes == 0)
+ return loop.RequestTermination();
+
+ if (llvm::Error err = to->Write(buf, num_bytes).takeError())
+ exitWithError(std::move(err));
+}
+
+void connectAndForwardIO(lldb_private::MainLoop &loop, ServerInfo &info,
+ IOObjectSP &input_sp, IOObjectSP &output_sp) {
+ auto uri = lldb_private::URI::Parse(info.connection_uri);
+ if (!uri)
+ exitWithError(createStringError("invalid connection_uri"));
+
+ std::optional<lldb_private::Socket::ProtocolModePair> protocol_and_mode =
+ lldb_private::Socket::GetProtocolAndMode(uri->scheme);
+
+ lldb_private::Status status;
+ std::unique_ptr<lldb_private::Socket> sock =
+ lldb_private::Socket::Create(protocol_and_mode->first, status);
+
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ if (uri->port && !uri->hostname.empty())
+ status = sock->Connect(
+ llvm::formatv("[{0}]:{1}", uri->hostname, *uri->port).str());
+ else
+ status = sock->Connect(uri->path);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ IOObjectSP sock_sp = std::move(sock);
+ auto input_handle = loop.RegisterReadObject(
+ input_sp, std::bind(forwardIO, std::placeholders::_1, input_sp, sock_sp),
+ status);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ auto socket_handle = loop.RegisterReadObject(
+ sock_sp, std::bind(forwardIO, std::placeholders::_1, sock_sp, output_sp),
+ status);
+ if (status.Fail())
+ exitWithError(status.takeError());
+
+ status = loop.Run();
+ if (status.Fail())
+ exitWithError(status.takeError());
+}
+
+llvm::ManagedStatic<lldb_private::SystemLifetimeManager> g_debugger_lifetime;
+
+} // namespace
+
+int main(int argc, char *argv[]) {
+ llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
+#if !defined(__APPLE__)
+ llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
+ " and include the crash backtrace.\n");
+#else
+ llvm::setBugReportMsg("PLEASE submit a bug report to " LLDB_BUG_REPORT_URL
+ " and include the crash report from "
+ "~/Library/Logs/DiagnosticReports/.\n");
+#endif
+
+#if defined(_WIN32)
+ // Windows opens stdout and stdin in text mode which converts \n to 13,10
+ // while the value is just 10 on Darwin/Linux. Setting the file mode to
+ // binary fixes this.
+ int result = _setmode(fileno(stdout), _O_BINARY);
+ assert(result);
+ result = _setmode(fileno(stdin), _O_BINARY);
+ UNUSED_IF_ASSERT_DISABLED(result);
+ assert(result);
+#endif
+
+ if (llvm::Error err = g_debugger_lifetime->Initialize(
+ std::make_unique<lldb_private::SystemInitializerCommon>(nullptr)))
+ exitWithError(std::move(err));
+
+ auto cleanup = make_scope_exit([] { g_debugger_lifetime->Terminate(); });
+
+ IOObjectSP input_sp = std::make_shared<NativeFile>(
+ fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);
+
+ IOObjectSP output_sp = std::make_shared<NativeFile>(
+ fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);
+
+ static MainLoop loop;
+
+ sys::SetInterruptFunction([]() {
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ });
+
+ auto existing_servers = ServerInfo::Load();
+
+ if (!existing_servers)
+ exitWithError(existing_servers.takeError());
+
+ // FIXME: Launch `lldb -o 'protocol start MCP'`.
+ if (existing_servers->empty())
+ exitWithError(createStringError("No MCP servers running"));
+
+ // FIXME: Support selecting a specific server.
+ if (existing_servers->size() != 1)
+ exitWithError(
+ createStringError("To many MCP servers running, picking a specific "
+ "one is not yet implemented."));
+
+ ServerInfo &info = existing_servers->front();
+ connectAndForwardIO(loop, info, input_sp, output_sp);
+
+ return EXIT_SUCCESS;
+}
diff --git a/lldb/tools/yaml2macho-core/CMakeLists.txt b/lldb/tools/yaml2macho-core/CMakeLists.txt
new file mode 100644
index 000000000000..5d8ee3645d52
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/CMakeLists.txt
@@ -0,0 +1,14 @@
+add_lldb_tool(yaml2macho-core
+ CoreSpec.cpp
+ LCNoteWriter.cpp
+ MemoryWriter.cpp
+ ThreadWriter.cpp
+ Utility.cpp
+ yaml2macho.cpp
+
+ LINK_COMPONENTS
+ Support
+ LINK_LIBS
+ lldbUtility
+ ${LLDB_SYSTEM_LIBS}
+)
diff --git a/lldb/tools/yaml2macho-core/CoreSpec.cpp b/lldb/tools/yaml2macho-core/CoreSpec.cpp
new file mode 100644
index 000000000000..22551a60b3a5
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/CoreSpec.cpp
@@ -0,0 +1,165 @@
+//===-- CoreSpec.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoreSpec.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <stdio.h>
+#include <string>
+
+using llvm::yaml::Input;
+using llvm::yaml::IO;
+using llvm::yaml::MappingTraits;
+
+template <> struct llvm::yaml::MappingTraits<RegisterNameAndValue> {
+ static void mapping(IO &io, RegisterNameAndValue &name_value) {
+ io.mapRequired("name", name_value.name);
+ io.mapRequired("value", name_value.value);
+ }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(RegisterNameAndValue)
+
+template <> struct llvm::yaml::ScalarEnumerationTraits<RegisterFlavor> {
+ static void enumeration(IO &io, RegisterFlavor &flavor) {
+ io.enumCase(flavor, "gpr", RegisterFlavor::GPR);
+ io.enumCase(flavor, "fpr", RegisterFlavor::FPR);
+ io.enumCase(flavor, "exc", RegisterFlavor::EXC);
+ }
+};
+
+template <> struct llvm::yaml::MappingTraits<RegisterSet> {
+ static void mapping(IO &io, RegisterSet &regset) {
+ io.mapRequired("flavor", regset.flavor);
+ io.mapRequired("registers", regset.registers);
+ }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(RegisterSet)
+
+template <> struct llvm::yaml::MappingTraits<Thread> {
+ static void mapping(IO &io, Thread &thread) {
+ io.mapRequired("regsets", thread.regsets);
+ }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(Thread)
+
+template <> struct llvm::yaml::MappingTraits<MemoryRegion> {
+ static void mapping(IO &io, MemoryRegion &memory) {
+ io.mapRequired("addr", memory.addr);
+ io.mapOptional("UInt8", memory.bytes);
+ io.mapOptional("UInt32", memory.words);
+ io.mapOptional("UInt64", memory.doublewords);
+
+ if (memory.bytes.size()) {
+ memory.type = MemoryType::UInt8;
+ memory.size = memory.bytes.size();
+ } else if (memory.words.size()) {
+ memory.type = MemoryType::UInt32;
+ memory.size = memory.words.size() * 4;
+ } else if (memory.doublewords.size()) {
+ memory.type = MemoryType::UInt64;
+ memory.size = memory.doublewords.size() * 8;
+ }
+ }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(MemoryRegion)
+
+template <> struct llvm::yaml::MappingTraits<Binary> {
+ static void mapping(IO &io, Binary &binary) {
+ io.mapOptional("name", binary.name);
+ io.mapRequired("uuid", binary.uuid);
+ std::optional<uint64_t> va, slide;
+ io.mapOptional("virtual-address", va);
+ io.mapOptional("slide", slide);
+ if (va && *va != UINT64_MAX) {
+ binary.value_is_slide = false;
+ binary.value = *va;
+ } else if (slide && *slide != UINT64_MAX) {
+ binary.value_is_slide = true;
+ binary.value = *slide;
+ } else {
+ fprintf(stderr,
+ "No virtual-address or slide specified for binary %s, aborting\n",
+ binary.uuid.c_str());
+ exit(1);
+ }
+ }
+};
+LLVM_YAML_IS_SEQUENCE_VECTOR(Binary)
+
+template <> struct llvm::yaml::MappingTraits<AddressableBits> {
+ static void mapping(IO &io, AddressableBits &addr_bits) {
+ std::optional<int> addressable_bits;
+ io.mapOptional("num-bits", addressable_bits);
+ if (addressable_bits) {
+ addr_bits.lowmem_bits = *addressable_bits;
+ addr_bits.highmem_bits = *addressable_bits;
+ } else {
+ io.mapOptional("lowmem-num-bits", addr_bits.lowmem_bits);
+ io.mapOptional("highmem-num-bits", addr_bits.highmem_bits);
+ }
+ }
+};
+
+template <> struct llvm::yaml::MappingTraits<CoreSpec> {
+ static void mapping(IO &io, CoreSpec &corespec) {
+ std::string cpuname;
+ io.mapRequired("cpu", cpuname);
+ if (cpuname == "armv7m") {
+ corespec.cputype = llvm::MachO::CPU_TYPE_ARM;
+ corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM_V7M;
+ } else if (cpuname == "armv7") {
+ corespec.cputype = llvm::MachO::CPU_TYPE_ARM;
+ corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM_ALL;
+ } else if (cpuname == "riscv") {
+ corespec.cputype = llvm::MachO::CPU_TYPE_RISCV;
+ corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_RISCV_ALL;
+ } else if (cpuname == "arm64") {
+ corespec.cputype = llvm::MachO::CPU_TYPE_ARM64;
+ corespec.cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM64_ALL;
+ } else {
+ fprintf(stderr, "Unrecognized cpu name %s, exiting.\n", cpuname.c_str());
+ exit(1);
+ }
+ io.mapOptional("threads", corespec.threads);
+ io.mapOptional("memory-regions", corespec.memory_regions);
+ if (corespec.cputype == llvm::MachO::CPU_TYPE_ARM ||
+ corespec.cputype == llvm::MachO::CPU_TYPE_RISCV)
+ corespec.wordsize = 4;
+ else if (corespec.cputype == llvm::MachO::CPU_TYPE_ARM64)
+ corespec.wordsize = 8;
+ else {
+ fprintf(stderr,
+ "Unrecognized cputype, could not set wordsize, exiting.\n");
+ exit(1);
+ }
+ io.mapOptional("addressable-bits", corespec.addressable_bits);
+ io.mapOptional("binaries", corespec.binaries);
+ if (corespec.addressable_bits) {
+ if (!corespec.addressable_bits->lowmem_bits)
+ corespec.addressable_bits->lowmem_bits = corespec.wordsize * 8;
+ if (!corespec.addressable_bits->highmem_bits)
+ corespec.addressable_bits->highmem_bits = corespec.wordsize * 8;
+ }
+ }
+};
+
+CoreSpec from_yaml(char *buf, size_t len) {
+ llvm::StringRef file_corespec_strref(buf, len);
+
+ Input yin(file_corespec_strref);
+
+ CoreSpec v;
+ yin >> v;
+
+ if (yin.error()) {
+ fprintf(stderr, "Unable to parse YAML, exiting\n");
+ exit(1);
+ }
+
+ return v;
+}
diff --git a/lldb/tools/yaml2macho-core/CoreSpec.h b/lldb/tools/yaml2macho-core/CoreSpec.h
new file mode 100644
index 000000000000..5c27cc96bdaa
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/CoreSpec.h
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// CoreSpec holds the internal representation of the data that will be
+/// written into the corefile. Theads, register sets within threads, registers
+/// within register sets. Block of memory. Metadata about the CPU or binaries
+/// that were present.
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_CORESPEC_H
+#define YAML2MACHOCOREFILE_CORESPEC_H
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+struct RegisterNameAndValue {
+ std::string name;
+ uint64_t value;
+};
+
+enum RegisterFlavor { GPR = 0, FPR, EXC };
+
+struct RegisterSet {
+ RegisterFlavor flavor;
+ std::vector<RegisterNameAndValue> registers;
+};
+
+struct Thread {
+ std::vector<RegisterSet> regsets;
+};
+
+enum MemoryType { UInt8 = 0, UInt32, UInt64 };
+
+struct MemoryRegion {
+ uint64_t addr;
+ MemoryType type;
+ uint32_t size;
+ // One of the following formats.
+ std::vector<uint8_t> bytes;
+ std::vector<uint32_t> words;
+ std::vector<uint64_t> doublewords;
+};
+
+struct AddressableBits {
+ std::optional<int> lowmem_bits;
+ std::optional<int> highmem_bits;
+};
+
+struct Binary {
+ std::string name;
+ std::string uuid;
+ bool value_is_slide;
+ uint64_t value;
+};
+
+struct CoreSpec {
+ uint32_t cputype;
+ uint32_t cpusubtype;
+ int wordsize;
+
+ std::vector<Thread> threads;
+ std::vector<MemoryRegion> memory_regions;
+
+ std::optional<AddressableBits> addressable_bits;
+ std::vector<Binary> binaries;
+
+ CoreSpec() : cputype(0), cpusubtype(0), wordsize(0) {}
+};
+
+CoreSpec from_yaml(char *buf, size_t len);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/LCNoteWriter.cpp b/lldb/tools/yaml2macho-core/LCNoteWriter.cpp
new file mode 100644
index 000000000000..824a1de7ddfa
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/LCNoteWriter.cpp
@@ -0,0 +1,97 @@
+//===-- LCNoteWriter.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LCNoteWriter.h"
+#include "Utility.h"
+#include "lldb/Utility/UUID.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include <ctype.h>
+#include <stdlib.h>
+
+void create_lc_note_binary_load_cmd(const CoreSpec &spec,
+ std::vector<uint8_t> &cmds,
+ const Binary &binary,
+ std::vector<uint8_t> &payload_bytes,
+ off_t data_offset) {
+
+ // Add the payload bytes to payload_bytes.
+ size_t starting_payload_size = payload_bytes.size();
+ add_uint32(payload_bytes, 1); // version
+ lldb_private::UUID uuid;
+ uuid.SetFromStringRef(binary.uuid);
+ for (size_t i = 0; i < uuid.GetBytes().size(); i++)
+ payload_bytes.push_back(uuid.GetBytes().data()[i]);
+ if (binary.value_is_slide) {
+ add_uint64(payload_bytes, UINT64_MAX); // address
+ add_uint64(payload_bytes, binary.value); // slide
+ } else {
+ add_uint64(payload_bytes, binary.value); // address
+ add_uint64(payload_bytes, UINT64_MAX); // slide
+ }
+ if (binary.name.empty()) {
+ payload_bytes.push_back(0); // name_cstring
+ } else {
+ size_t len = binary.name.size();
+ for (size_t i = 0; i < len; i++)
+ payload_bytes.push_back(binary.name[i]);
+ payload_bytes.push_back(0); // name_cstring
+ }
+
+ size_t payload_size = payload_bytes.size() - starting_payload_size;
+ // Pad out the entry to a 4-byte aligned size.
+ if (payload_bytes.size() % 4 != 0) {
+ size_t pad_bytes =
+ ((payload_bytes.size() + 4 - 1) & ~(4 - 1)) - payload_bytes.size();
+ for (size_t i = 0; i < pad_bytes; i++)
+ payload_bytes.push_back(0);
+ }
+
+ // Add the load command bytes to cmds.
+ add_uint32(cmds, llvm::MachO::LC_NOTE);
+ add_uint32(cmds, sizeof(struct llvm::MachO::note_command));
+ char cmdname[16];
+ memset(cmdname, '\0', sizeof(cmdname));
+ strcpy(cmdname, "load binary");
+ for (int i = 0; i < 16; i++)
+ cmds.push_back(cmdname[i]);
+ add_uint64(cmds, data_offset);
+ add_uint64(cmds, payload_size);
+}
+
+void create_lc_note_addressable_bits(const CoreSpec &spec,
+ std::vector<uint8_t> &cmds,
+ const AddressableBits &addr_bits,
+ std::vector<uint8_t> &payload_bytes,
+ off_t data_offset) {
+ // Add the payload bytes to payload_bytes.
+ size_t starting_payload_size = payload_bytes.size();
+ add_uint32(payload_bytes, 4); // version
+
+ add_uint32(payload_bytes, *addr_bits.lowmem_bits); // low memory
+ add_uint32(payload_bytes, *addr_bits.highmem_bits); // high memory
+ add_uint32(payload_bytes, 0); // reserved
+ size_t payload_size = payload_bytes.size() - starting_payload_size;
+ // Pad out the entry to a 4-byte aligned size.
+ if (payload_bytes.size() % 4 != 0) {
+ size_t pad_bytes =
+ ((payload_bytes.size() + 4 - 1) & ~(4 - 1)) - payload_bytes.size();
+ for (size_t i = 0; i < pad_bytes; i++)
+ payload_bytes.push_back(0);
+ }
+
+ // Add the load command bytes to cmds.
+ add_uint32(cmds, llvm::MachO::LC_NOTE);
+ add_uint32(cmds, sizeof(struct llvm::MachO::note_command));
+ char cmdname[16];
+ memset(cmdname, '\0', sizeof(cmdname));
+ strcpy(cmdname, "addrable bits");
+ for (int i = 0; i < 16; i++)
+ cmds.push_back(cmdname[i]);
+ add_uint64(cmds, data_offset);
+ add_uint64(cmds, payload_size);
+}
diff --git a/lldb/tools/yaml2macho-core/LCNoteWriter.h b/lldb/tools/yaml2macho-core/LCNoteWriter.h
new file mode 100644
index 000000000000..ae04faabe828
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/LCNoteWriter.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// Functions to add an LC_NOTE load command to the corefile's load commands,
+/// and supply the payload of that LC_NOTE separately.
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_LCNOTEWRITER_H
+#define YAML2MACHOCOREFILE_LCNOTEWRITER_H
+
+#include "CoreSpec.h"
+
+#include <stdio.h>
+#include <vector>
+
+void create_lc_note_binary_load_cmd(const CoreSpec &spec,
+ std::vector<uint8_t> &cmds,
+ const Binary &binary,
+ std::vector<uint8_t> &payload_bytes,
+ off_t data_offset);
+
+void create_lc_note_addressable_bits(const CoreSpec &spec,
+ std::vector<uint8_t> &cmds,
+ const AddressableBits &addr_bits,
+ std::vector<uint8_t> &payload_bytes,
+ off_t data_offset);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/MemoryWriter.cpp b/lldb/tools/yaml2macho-core/MemoryWriter.cpp
new file mode 100644
index 000000000000..4ace2894b6be
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/MemoryWriter.cpp
@@ -0,0 +1,56 @@
+//===-- MemoryWriter.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MemoryWriter.h"
+#include "CoreSpec.h"
+#include "Utility.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+void create_lc_segment_cmd(const CoreSpec &spec, std::vector<uint8_t> &cmds,
+ const MemoryRegion &memory, off_t data_offset) {
+ if (spec.wordsize == 8) {
+ // Add the bytes for a segment_command_64 from <mach-o/loader.h>
+ add_uint32(cmds, llvm::MachO::LC_SEGMENT_64);
+ add_uint32(cmds, sizeof(struct llvm::MachO::segment_command_64));
+ for (int i = 0; i < 16; i++)
+ cmds.push_back(0);
+ add_uint64(cmds, memory.addr); // segment_command_64.vmaddr
+ add_uint64(cmds, memory.size); // segment_command_64.vmsize
+ add_uint64(cmds, data_offset); // segment_command_64.fileoff
+ add_uint64(cmds, memory.size); // segment_command_64.filesize
+ } else {
+ // Add the bytes for a segment_command from <mach-o/loader.h>
+ add_uint32(cmds, llvm::MachO::LC_SEGMENT);
+ add_uint32(cmds, sizeof(struct llvm::MachO::segment_command));
+ for (int i = 0; i < 16; i++)
+ cmds.push_back(0);
+ add_uint32(cmds, memory.addr); // segment_command_64.vmaddr
+ add_uint32(cmds, memory.size); // segment_command_64.vmsize
+ add_uint32(cmds, data_offset); // segment_command_64.fileoff
+ add_uint32(cmds, memory.size); // segment_command_64.filesize
+ }
+ add_uint32(cmds, 3); // segment_command_64.maxprot
+ add_uint32(cmds, 3); // segment_command_64.initprot
+ add_uint32(cmds, 0); // segment_command_64.nsects
+ add_uint32(cmds, 0); // segment_command_64.flags
+}
+
+void create_memory_bytes(const CoreSpec &spec, const MemoryRegion &memory,
+ std::vector<uint8_t> &buf) {
+ if (memory.type == MemoryType::UInt8)
+ for (uint8_t byte : memory.bytes)
+ buf.push_back(byte);
+
+ if (memory.type == MemoryType::UInt32)
+ for (uint32_t word : memory.words)
+ add_uint32(buf, word);
+
+ if (memory.type == MemoryType::UInt64)
+ for (uint64_t word : memory.doublewords)
+ add_uint64(buf, word);
+}
diff --git a/lldb/tools/yaml2macho-core/MemoryWriter.h b/lldb/tools/yaml2macho-core/MemoryWriter.h
new file mode 100644
index 000000000000..2bef00cdb764
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/MemoryWriter.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// Functions to emit the LC_SEGMENT load command, and to provide the bytes
+/// that appear later in the corefile.
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_MEMORYWRITER_H
+#define YAML2MACHOCOREFILE_MEMORYWRITER_H
+
+#include "CoreSpec.h"
+
+#include <vector>
+
+void create_lc_segment_cmd(const CoreSpec &spec, std::vector<uint8_t> &cmds,
+ const MemoryRegion &memory, off_t data_offset);
+
+void create_memory_bytes(const CoreSpec &spec, const MemoryRegion &memory,
+ std::vector<uint8_t> &buf);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/ThreadWriter.cpp b/lldb/tools/yaml2macho-core/ThreadWriter.cpp
new file mode 100644
index 000000000000..40c70d92970c
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/ThreadWriter.cpp
@@ -0,0 +1,200 @@
+//===-- ThreadWriter.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ThreadWriter.h"
+#include "CoreSpec.h"
+#include "Utility.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include <algorithm>
+#include <stdio.h>
+
+#define ARM_THREAD_STATE 1
+#define ARM_THREAD_STATE_COUNT 17
+#define ARM_EXCEPTION_STATE 3
+#define ARM_EXCEPTION_STATE_COUNT 3
+
+std::vector<RegisterNameAndValue>::const_iterator
+find_by_name(std::vector<RegisterNameAndValue>::const_iterator first,
+ std::vector<RegisterNameAndValue>::const_iterator last,
+ const char *name) {
+ for (; first != last; ++first)
+ if (first->name == name)
+ return first;
+ return last;
+}
+
+void add_reg_value(CoreSpec &spec, std::vector<uint8_t> &buf,
+ const std::vector<RegisterNameAndValue> &registers,
+ const char *regname, int regsize) {
+ const auto it = find_by_name(registers.begin(), registers.end(), regname);
+ if (it != registers.end()) {
+ if (regsize == 8)
+ add_uint64(buf, it->value);
+ else
+ add_uint32(buf, it->value);
+ } else {
+ if (regsize == 8)
+ add_uint64(buf, 0);
+ else
+ add_uint32(buf, 0);
+ }
+}
+
+void add_reg_value_32(CoreSpec &spec, std::vector<uint8_t> &buf,
+ const std::vector<RegisterNameAndValue> &registers,
+ const char *regname) {
+ add_reg_value(spec, buf, registers, regname, 4);
+}
+
+void add_reg_value_64(CoreSpec &spec, std::vector<uint8_t> &buf,
+ const std::vector<RegisterNameAndValue> &registers,
+ const char *regname) {
+ add_reg_value(spec, buf, registers, regname, 8);
+}
+
+void add_lc_threads_armv7(CoreSpec &spec,
+ std::vector<std::vector<uint8_t>> &load_commands) {
+ for (const Thread &th : spec.threads) {
+ std::vector<uint8_t> lc;
+ int size_of_all_flavors = 0;
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR)
+ size_of_all_flavors += (ARM_THREAD_STATE_COUNT * 4);
+ if (rs.flavor == RegisterFlavor::EXC)
+ size_of_all_flavors += (ARM_EXCEPTION_STATE_COUNT * 4);
+ }
+ int cmdsize = 4 * 2; // cmd, cmdsize
+ cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register flavor)
+ cmdsize += size_of_all_flavors; // size of all the register set data
+
+ add_uint32(lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+ add_uint32(lc, cmdsize); // thread_command.cmdsize
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR) {
+ add_uint32(lc, ARM_THREAD_STATE); // thread_command.flavor
+ add_uint32(lc, ARM_THREAD_STATE_COUNT); // thread_command.count
+ const char *names[] = {"r0", "r1", "r2", "r3", "r4", "r5",
+ "r6", "r7", "r8", "r9", "r10", "r11",
+ "r12", "sp", "lr", "pc", "cpsr", nullptr};
+ for (int i = 0; names[i]; i++)
+ add_reg_value_32(spec, lc, rs.registers, names[i]);
+ }
+ if (rs.flavor == RegisterFlavor::EXC) {
+ add_uint32(lc, ARM_EXCEPTION_STATE); // thread_command.flavor
+ add_uint32(lc, ARM_EXCEPTION_STATE_COUNT); // thread_command.count
+ const char *names[] = {"far", "esr", "exception", nullptr};
+ for (int i = 0; names[i]; i++)
+ add_reg_value_32(spec, lc, rs.registers, names[i]);
+ }
+ }
+ load_commands.push_back(lc);
+ }
+}
+
+#define ARM_THREAD_STATE64 6
+#define ARM_THREAD_STATE64_COUNT 68
+#define ARM_EXCEPTION_STATE64 7
+#define ARM_EXCEPTION_STATE64_COUNT 4
+
+void add_lc_threads_arm64(CoreSpec &spec,
+ std::vector<std::vector<uint8_t>> &load_commands) {
+ for (const Thread &th : spec.threads) {
+ std::vector<uint8_t> lc;
+ int size_of_all_flavors = 0;
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR)
+ size_of_all_flavors += (ARM_THREAD_STATE64_COUNT * 4);
+ if (rs.flavor == RegisterFlavor::EXC)
+ size_of_all_flavors += (ARM_EXCEPTION_STATE64_COUNT * 4);
+ }
+ int cmdsize = 4 * 2; // cmd, cmdsize
+ cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register flavor)
+ cmdsize += size_of_all_flavors; // size of all the register set data
+
+ add_uint32(lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+ add_uint32(lc, cmdsize); // thread_command.cmdsize
+
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR) {
+ add_uint32(lc, ARM_THREAD_STATE64); // thread_command.flavor
+ add_uint32(lc, ARM_THREAD_STATE64_COUNT); // thread_command.count
+ const char *names[] = {"x0", "x1", "x2", "x3", "x4", "x5", "x6",
+ "x7", "x8", "x9", "x10", "x11", "x12", "x13",
+ "x14", "x15", "x16", "x17", "x18", "x19", "x20",
+ "x21", "x22", "x23", "x24", "x25", "x26", "x27",
+ "x28", "fp", "lr", "sp", "pc", nullptr};
+ for (int i = 0; names[i]; i++)
+ add_reg_value_64(spec, lc, rs.registers, names[i]);
+
+ // cpsr is a 4-byte reg
+ add_reg_value_32(spec, lc, rs.registers, "cpsr");
+ // the 4 bytes of zeroes
+ add_uint32(lc, 0);
+ }
+ if (rs.flavor == RegisterFlavor::EXC) {
+ add_uint32(lc, ARM_EXCEPTION_STATE64); // thread_command.flavor
+ add_uint32(lc,
+ ARM_EXCEPTION_STATE64_COUNT); // thread_command.count
+ add_reg_value_64(spec, lc, rs.registers, "far");
+ add_reg_value_32(spec, lc, rs.registers, "esr");
+ add_reg_value_32(spec, lc, rs.registers, "exception");
+ }
+ }
+ load_commands.push_back(lc);
+ }
+}
+
+#define RV32_THREAD_STATE 2
+#define RV32_THREAD_STATE_COUNT 33
+
+void add_lc_threads_riscv(CoreSpec &spec,
+ std::vector<std::vector<uint8_t>> &load_commands) {
+ for (const Thread &th : spec.threads) {
+ std::vector<uint8_t> lc;
+ int size_of_all_flavors = 0;
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR)
+ size_of_all_flavors += (RV32_THREAD_STATE_COUNT * 4);
+ }
+ int cmdsize = 4 * 2; // cmd, cmdsize
+ cmdsize += 4 * 2 * th.regsets.size(); // flavor, count (per register flavor)
+ cmdsize += size_of_all_flavors; // size of all the register set data
+
+ add_uint32(lc, llvm::MachO::LC_THREAD); // thread_command.cmd
+ add_uint32(lc, cmdsize); // thread_command.cmdsize
+ for (const RegisterSet &rs : th.regsets) {
+ if (rs.flavor == RegisterFlavor::GPR) {
+ add_uint32(lc, RV32_THREAD_STATE); // thread_command.flavor
+ add_uint32(lc, RV32_THREAD_STATE_COUNT); // thread_command.count
+ const char *names[] = {"zero", "ra", "sp", "gp", "tp", "t0", "t1",
+ "t2", "fp", "s1", "a0", "a1", "a2", "a3",
+ "a4", "a5", "a6", "a7", "s2", "s3", "s4",
+ "s5", "s6", "s7", "s8", "s9", "s10", "s11",
+ "t3", "t4", "t5", "t6", "pc", nullptr};
+ for (int i = 0; names[i]; i++)
+ add_reg_value_32(spec, lc, rs.registers, names[i]);
+ }
+ }
+ load_commands.push_back(lc);
+ }
+}
+
+void add_lc_threads(CoreSpec &spec,
+ std::vector<std::vector<uint8_t>> &load_commands) {
+ if (spec.cputype == llvm::MachO::CPU_TYPE_ARM)
+ add_lc_threads_armv7(spec, load_commands);
+ else if (spec.cputype == llvm::MachO::CPU_TYPE_ARM64)
+ add_lc_threads_arm64(spec, load_commands);
+ else if (spec.cputype == llvm::MachO::CPU_TYPE_RISCV)
+ add_lc_threads_riscv(spec, load_commands);
+ else {
+ fprintf(stderr,
+ "Unrecognized cputype, could not write LC_THREAD. Exiting.\n");
+ exit(1);
+ }
+}
diff --git a/lldb/tools/yaml2macho-core/ThreadWriter.h b/lldb/tools/yaml2macho-core/ThreadWriter.h
new file mode 100644
index 000000000000..58ce4abdf0d1
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/ThreadWriter.h
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// Functions to emit LC_THREAD bytes to the corefile's Mach-O load commands,
+/// specifying the threads, the register sets ("flavors") within those threads,
+/// and all of the registers within those register sets.
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_THREADWRITER_H
+#define YAML2MACHOCOREFILE_THREADWRITER_H
+
+#include "CoreSpec.h"
+
+#include <vector>
+
+void add_lc_threads(CoreSpec &spec,
+ std::vector<std::vector<uint8_t>> &load_commands);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/Utility.cpp b/lldb/tools/yaml2macho-core/Utility.cpp
new file mode 100644
index 000000000000..2414c2f03932
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/Utility.cpp
@@ -0,0 +1,22 @@
+//===-- Utility.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Utility.h"
+#include "CoreSpec.h"
+
+void add_uint64(std::vector<uint8_t> &buf, uint64_t val) {
+ uint8_t *p = reinterpret_cast<uint8_t *>(&val);
+ for (int i = 0; i < 8; i++)
+ buf.push_back(*p++);
+}
+
+void add_uint32(std::vector<uint8_t> &buf, uint32_t val) {
+ uint8_t *p = reinterpret_cast<uint8_t *>(&val);
+ for (int i = 0; i < 4; i++)
+ buf.push_back(*p++);
+}
diff --git a/lldb/tools/yaml2macho-core/Utility.h b/lldb/tools/yaml2macho-core/Utility.h
new file mode 100644
index 000000000000..ff05858c4dee
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/Utility.h
@@ -0,0 +1,18 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef YAML2MACHOCOREFILE_UTILITY_H
+#define YAML2MACHOCOREFILE_UTILITY_H
+
+#include "CoreSpec.h"
+#include <vector>
+
+void add_uint64(std::vector<uint8_t> &buf, uint64_t val);
+void add_uint32(std::vector<uint8_t> &buf, uint32_t val);
+
+#endif
diff --git a/lldb/tools/yaml2macho-core/yaml2macho.cpp b/lldb/tools/yaml2macho-core/yaml2macho.cpp
new file mode 100644
index 000000000000..85979a37d167
--- /dev/null
+++ b/lldb/tools/yaml2macho-core/yaml2macho.cpp
@@ -0,0 +1,250 @@
+//===-- main.cppp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "CoreSpec.h"
+#include "LCNoteWriter.h"
+#include "MemoryWriter.h"
+#include "ThreadWriter.h"
+#include "Utility.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/CommandLine.h"
+#include <stdio.h>
+#include <string>
+#include <sys/stat.h>
+
+std::vector<std::string> get_fields_from_delimited_string(std::string str,
+ const char delim) {
+ std::vector<std::string> result;
+ std::string::size_type prev = std::string::npos;
+ std::string::size_type next = str.find(delim);
+ if (str.empty()) {
+ return result;
+ }
+ if (next == std::string::npos) {
+ result.push_back(str);
+ } else {
+ result.push_back(std::string(str, 0, next));
+ prev = next;
+ while ((next = str.find(delim, prev + 1)) != std::string::npos) {
+ result.push_back(std::string(str, prev + 1, next - prev - 1));
+ prev = next;
+ }
+ result.push_back(std::string(str, prev + 1));
+ }
+ return result;
+}
+
+llvm::cl::opt<std::string> InputFilename("i", llvm::cl::Required,
+ llvm::cl::desc("input yaml filename"),
+ llvm::cl::value_desc("input"));
+llvm::cl::opt<std::string>
+ OutputFilename("o", llvm::cl::Required,
+ llvm::cl::desc("output core filenames"),
+ llvm::cl::value_desc("output"));
+llvm::cl::list<std::string>
+ UUIDs("u", llvm::cl::desc("uuid of binary loaded at slide 0"),
+ llvm::cl::value_desc("uuid"));
+llvm::cl::list<std::string>
+ UUIDAndVAs("L", llvm::cl::desc("UUID,virtual-address-loaded-at"),
+ llvm::cl::value_desc("--uuid-and-load-addr"));
+llvm::cl::opt<int>
+ AddressableBitsOverride("A",
+ llvm::cl::desc("number of bits used in addressing"),
+ llvm::cl::value_desc("--address-bits"));
+
+int main(int argc, char **argv) {
+ llvm::cl::ParseCommandLineOptions(argc, argv);
+
+ if (InputFilename.empty() || OutputFilename.empty()) {
+ fprintf(stderr, "Missing input or outpur file.\n");
+ exit(1);
+ }
+
+ struct stat sb;
+
+ if (stat(InputFilename.c_str(), &sb) == -1) {
+ fprintf(stderr, "Unable to stat %s, exiting\n", InputFilename.c_str());
+ exit(1);
+ }
+
+ FILE *input = fopen(InputFilename.c_str(), "r");
+ if (!input) {
+ fprintf(stderr, "Unable to open %s, exiting\n", InputFilename.c_str());
+ exit(1);
+ }
+ auto file_corespec = std::make_unique<char[]>(sb.st_size);
+ if (fread(file_corespec.get(), sb.st_size, 1, input) != 1) {
+ fprintf(stderr, "Unable to read all of %s, exiting\n",
+ InputFilename.c_str());
+ exit(1);
+ }
+ CoreSpec spec = from_yaml(file_corespec.get(), sb.st_size);
+ fclose(input);
+
+ for (const std::string &uuid : UUIDs) {
+ Binary binary;
+ binary.uuid = uuid;
+ binary.value = 0;
+ binary.value_is_slide = true;
+ spec.binaries.push_back(binary);
+ }
+
+ for (const std::string &uuid_and_va : UUIDAndVAs) {
+ std::vector<std::string> parts =
+ get_fields_from_delimited_string(uuid_and_va, ',');
+
+ std::string uuid = parts[0];
+ uint64_t va = std::strtoull(parts[1].c_str(), nullptr, 16);
+ Binary binary;
+ binary.uuid = uuid;
+ binary.value = va;
+ binary.value_is_slide = false;
+ spec.binaries.push_back(binary);
+ }
+
+ if (AddressableBitsOverride) {
+ AddressableBits bits;
+ bits.lowmem_bits = bits.highmem_bits = AddressableBitsOverride;
+ spec.addressable_bits = bits;
+ }
+
+ // An array of load commands
+ std::vector<std::vector<uint8_t>> load_commands;
+
+ // An array of corefile contents (memory regions)
+ std::vector<uint8_t> payload;
+
+ // First add all the load commands / payload so we can figure out how large
+ // the load commands will be.
+
+ add_lc_threads(spec, load_commands);
+ for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+ std::vector<uint8_t> segment_command_bytes;
+ create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i],
+ 0);
+ load_commands.push_back(segment_command_bytes);
+ }
+
+ if (spec.binaries.size() > 0)
+ for (const Binary &binary : spec.binaries) {
+ std::vector<uint8_t> segment_command_bytes;
+ std::vector<uint8_t> payload_bytes;
+ create_lc_note_binary_load_cmd(spec, segment_command_bytes, binary,
+ payload_bytes, 0);
+ load_commands.push_back(segment_command_bytes);
+ }
+ if (spec.addressable_bits) {
+ std::vector<uint8_t> segment_command_bytes;
+ std::vector<uint8_t> payload_bytes;
+ create_lc_note_addressable_bits(spec, segment_command_bytes,
+ *spec.addressable_bits, payload_bytes, 0);
+ load_commands.push_back(segment_command_bytes);
+ }
+
+ off_t size_of_load_commands = 0;
+ for (const auto &lc : load_commands)
+ size_of_load_commands += lc.size();
+
+ off_t header_and_load_cmd_room =
+ sizeof(llvm::MachO::mach_header_64) + size_of_load_commands;
+ off_t initial_payload_fileoff = header_and_load_cmd_room;
+ initial_payload_fileoff = (initial_payload_fileoff + 4096 - 1) & ~(4096 - 1);
+ off_t payload_fileoff = initial_payload_fileoff;
+
+ // Erase the load commands / payload now that we know how much space is
+ // needed, redo it with real values.
+ load_commands.clear();
+ payload.clear();
+
+ add_lc_threads(spec, load_commands);
+ for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+ std::vector<uint8_t> segment_command_bytes;
+ create_lc_segment_cmd(spec, segment_command_bytes, spec.memory_regions[i],
+ payload_fileoff);
+ load_commands.push_back(segment_command_bytes);
+ payload_fileoff += spec.memory_regions[i].size;
+ payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+ }
+
+ off_t payload_fileoff_before_lcnotes = payload_fileoff;
+ std::vector<uint8_t> lc_note_payload_bytes;
+ if (spec.binaries.size() > 0)
+ for (const Binary &binary : spec.binaries) {
+ std::vector<uint8_t> segment_command_bytes;
+ std::vector<uint8_t> payload_bytes;
+ create_lc_note_binary_load_cmd(spec, segment_command_bytes, binary,
+ lc_note_payload_bytes, payload_fileoff);
+ payload_fileoff =
+ payload_fileoff_before_lcnotes + lc_note_payload_bytes.size();
+ load_commands.push_back(segment_command_bytes);
+ }
+ if (spec.addressable_bits) {
+ std::vector<uint8_t> segment_command_bytes;
+ std::vector<uint8_t> payload_bytes;
+ create_lc_note_addressable_bits(spec, segment_command_bytes,
+ *spec.addressable_bits,
+ lc_note_payload_bytes, payload_fileoff);
+ payload_fileoff =
+ payload_fileoff_before_lcnotes + lc_note_payload_bytes.size();
+ load_commands.push_back(segment_command_bytes);
+ }
+
+ // Realign our payload offset if we added any LC_NOTEs.
+ if (lc_note_payload_bytes.size() > 0)
+ payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+
+ FILE *f = fopen(OutputFilename.c_str(), "wb");
+ if (f == nullptr) {
+ fprintf(stderr, "Unable to open file %s for writing\n",
+ OutputFilename.c_str());
+ exit(1);
+ }
+
+ std::vector<uint8_t> mh;
+ // Write the fields of a mach_header_64 struct
+ if (spec.wordsize == 8)
+ add_uint32(mh, llvm::MachO::MH_MAGIC_64); // magic
+ else
+ add_uint32(mh, llvm::MachO::MH_MAGIC); // magic
+ add_uint32(mh, spec.cputype); // cputype
+ add_uint32(mh, spec.cpusubtype); // cpusubtype
+ add_uint32(mh, llvm::MachO::MH_CORE); // filetype
+ add_uint32(mh, load_commands.size()); // ncmds
+ add_uint32(mh, size_of_load_commands); // sizeofcmds
+ add_uint32(mh, 0); // flags
+ if (spec.wordsize == 8)
+ add_uint32(mh, 0); // reserved
+
+ fwrite(mh.data(), mh.size(), 1, f);
+
+ for (const auto &lc : load_commands)
+ fwrite(lc.data(), lc.size(), 1, f);
+
+ // Reset the payload offset back to the first one.
+ payload_fileoff = initial_payload_fileoff;
+ if (spec.memory_regions.size() > 0) {
+ for (size_t i = 0; i < spec.memory_regions.size(); i++) {
+ std::vector<uint8_t> bytes;
+ create_memory_bytes(spec, spec.memory_regions[i], bytes);
+ fseek(f, payload_fileoff, SEEK_SET);
+ fwrite(bytes.data(), bytes.size(), 1, f);
+
+ payload_fileoff += bytes.size();
+ payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+ }
+ }
+
+ if (lc_note_payload_bytes.size() > 0) {
+ fseek(f, payload_fileoff, SEEK_SET);
+ fwrite(lc_note_payload_bytes.data(), lc_note_payload_bytes.size(), 1, f);
+ payload_fileoff += lc_note_payload_bytes.size();
+ payload_fileoff = (payload_fileoff + 4096 - 1) & ~(4096 - 1);
+ }
+
+ fclose(f);
+}
diff --git a/lldb/unittests/API/SBCommandInterpreterTest.cpp b/lldb/unittests/API/SBCommandInterpreterTest.cpp
index 5651e1c3dc63..c420109e55d6 100644
--- a/lldb/unittests/API/SBCommandInterpreterTest.cpp
+++ b/lldb/unittests/API/SBCommandInterpreterTest.cpp
@@ -60,7 +60,7 @@ TEST_F(SBCommandInterpreterTest, SingleWordCommand) {
SBCommandReturnObject result;
interp.HandleCommand("", result);
EXPECT_FALSE(result.Succeeded());
- EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+ EXPECT_STREQ(result.GetError(), "error: no auto repeat\n");
}
// Now we test a command with autorepeat
@@ -98,7 +98,7 @@ TEST_F(SBCommandInterpreterTest, MultiWordCommand) {
SBCommandReturnObject result;
interp.HandleCommand("", result);
EXPECT_FALSE(result.Succeeded());
- EXPECT_STREQ(result.GetError(), "error: No auto repeat.\n");
+ EXPECT_STREQ(result.GetError(), "error: no auto repeat\n");
}
// We first test a subcommand with autorepeat
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index 5533c73c3de8..4c5267ae25b7 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -79,10 +79,6 @@ add_subdirectory(Utility)
add_subdirectory(ValueObject)
add_subdirectory(tools)
-if(LLDB_ENABLE_PROTOCOL_SERVERS)
- add_subdirectory(ProtocolServer)
-endif()
-
if(LLDB_CAN_USE_DEBUGSERVER AND LLDB_TOOL_DEBUGSERVER_BUILD AND NOT LLDB_USE_SYSTEM_DEBUGSERVER)
add_subdirectory(debugserver)
endif()
diff --git a/lldb/unittests/Core/FormatEntityTest.cpp b/lldb/unittests/Core/FormatEntityTest.cpp
index e056b6fe7de5..309294a50188 100644
--- a/lldb/unittests/Core/FormatEntityTest.cpp
+++ b/lldb/unittests/Core/FormatEntityTest.cpp
@@ -117,6 +117,7 @@ constexpr llvm::StringRef lookupStrings[] = {
"${frame.no-debug}",
"${frame.reg.*}",
"${frame.is-artificial}",
+ "${frame.kind}",
"${function.id}",
"${function.name}",
"${function.name-without-args}",
diff --git a/lldb/unittests/DAP/DAPTest.cpp b/lldb/unittests/DAP/DAPTest.cpp
index d5a9591ad0a4..2090fe6896d6 100644
--- a/lldb/unittests/DAP/DAPTest.cpp
+++ b/lldb/unittests/DAP/DAPTest.cpp
@@ -28,6 +28,7 @@ TEST_F(DAPTest, SendProtocolMessages) {
/*log=*/nullptr,
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/{},
+ /*no_lldbinit=*/false,
/*client_name=*/"test_client",
/*transport=*/*transport,
/*loop=*/loop,
diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp
index 54ac27da694e..ba7baf210379 100644
--- a/lldb/unittests/DAP/TestBase.cpp
+++ b/lldb/unittests/DAP/TestBase.cpp
@@ -55,6 +55,7 @@ void DAPTestBase::SetUp() {
/*log=*/log.get(),
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/std::vector<std::string>(),
+ /*no_lldbinit=*/false,
/*client_name=*/"test_client",
/*transport=*/*transport, /*loop=*/loop);
}
diff --git a/lldb/unittests/Editline/EditlineTest.cpp b/lldb/unittests/Editline/EditlineTest.cpp
index 6c5a0c907a33..2875f4ee7e6b 100644
--- a/lldb/unittests/Editline/EditlineTest.cpp
+++ b/lldb/unittests/Editline/EditlineTest.cpp
@@ -8,6 +8,7 @@
#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
+#include "lldb/Host/HostInfo.h"
#if LLDB_ENABLE_LIBEDIT
@@ -244,7 +245,7 @@ void EditlineAdapter::ConsumeAllOutput() {
}
class EditlineTestFixture : public ::testing::Test {
- SubsystemRAII<FileSystem> subsystems;
+ SubsystemRAII<FileSystem, HostInfo> subsystems;
EditlineAdapter _el_adapter;
std::shared_ptr<std::thread> _sp_output_thread;
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp
index 12f6dd515fd1..ceb567c28ab9 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -23,15 +23,15 @@ struct LabelTestCase {
static LabelTestCase g_label_test_cases[] = {
// Failure modes
- {"bar:0x0:0x0:_Z3foov",
+ {"bar:blah:0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found 'bar' "
"instead."}},
- {"$__lldb_func :0x0:0x0:_Z3foov",
+ {"$__lldb_func :blah:0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found "
"'$__lldb_func ' instead."}},
- {"$__lldb_funcc:0x0:0x0:_Z3foov",
+ {"$__lldb_funcc:blah:0x0:0x0:_Z3foov",
{},
{"expected function call label prefix '$__lldb_func' but found "
"'$__lldb_funcc' instead."}},
@@ -39,47 +39,52 @@ static LabelTestCase g_label_test_cases[] = {
{"foo", {}, {"malformed function call label."}},
{"$__lldb_func", {}, {"malformed function call label."}},
{"$__lldb_func:", {}, {"malformed function call label."}},
- {"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}},
- {"$__lldb_func:abc:0x0:_Z3foov",
+ {"$__lldb_func:blah", {}, {"malformed function call label."}},
+ {"$__lldb_func:blah:0x0", {}, {"malformed function call label."}},
+ {"$__lldb_func:111:0x0:0x0", {}, {"malformed function call label."}},
+ {"$__lldb_func:111:abc:0x0:_Z3foov",
{},
{"failed to parse module ID from 'abc'."}},
- {"$__lldb_func:-1:0x0:_Z3foov",
+ {"$__lldb_func:111:-1:0x0:_Z3foov",
{},
{"failed to parse module ID from '-1'."}},
- {"$__lldb_func:0x0invalid:0x0:_Z3foov",
+ {"$__lldb_func:111:0x0invalid:0x0:_Z3foov",
{},
{"failed to parse module ID from '0x0invalid'."}},
- {"$__lldb_func:0x0 :0x0:_Z3foov",
+ {"$__lldb_func:111:0x0 :0x0:_Z3foov",
{},
{"failed to parse module ID from '0x0 '."}},
- {"$__lldb_func:0x0:abc:_Z3foov",
+ {"$__lldb_func:blah:0x0:abc:_Z3foov",
{},
{"failed to parse symbol ID from 'abc'."}},
- {"$__lldb_func:0x5:-1:_Z3foov",
+ {"$__lldb_func:blah:0x5:-1:_Z3foov",
{},
{"failed to parse symbol ID from '-1'."}},
- {"$__lldb_func:0x5:0x0invalid:_Z3foov",
+ {"$__lldb_func:blah:0x5:0x0invalid:_Z3foov",
{},
{"failed to parse symbol ID from '0x0invalid'."}},
- {"$__lldb_func:0x5:0x0 :_Z3foov",
+ {"$__lldb_func:blah:0x5:0x0 :_Z3foov",
{},
{"failed to parse symbol ID from '0x0 '."}},
- {"$__lldb_func:0x0:0x0:_Z3foov",
+ {"$__lldb_func:blah:0x0:0x0:_Z3foov",
{
+ /*.discriminator=*/"blah",
/*.module_id=*/0x0,
/*.symbol_id=*/0x0,
/*.lookup_name=*/"_Z3foov",
},
{}},
- {"$__lldb_func:0x0:0x0:abc:def:::a",
+ {"$__lldb_func::0x0:0x0:abc:def:::a",
{
+ /*.discriminator=*/"",
/*.module_id=*/0x0,
/*.symbol_id=*/0x0,
/*.lookup_name=*/"abc:def:::a",
},
{}},
- {"$__lldb_func:0xd2:0xf0:$__lldb_func",
+ {"$__lldb_func:0x45:0xd2:0xf0:$__lldb_func",
{
+ /*.discriminator=*/"0x45",
/*.module_id=*/0xd2,
/*.symbol_id=*/0xf0,
/*.lookup_name=*/"$__lldb_func",
@@ -106,6 +111,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
EXPECT_EQ(decoded_or_err->toString(), encoded);
EXPECT_EQ(label_str, encoded);
+ EXPECT_EQ(decoded_or_err->discriminator, label.discriminator);
EXPECT_EQ(decoded_or_err->module_id, label.module_id);
EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id);
EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name);
@@ -113,6 +119,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
auto roundtrip_or_err = FunctionCallLabel::fromString(label_str);
EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded());
+ EXPECT_EQ(roundtrip_or_err->discriminator, label.discriminator);
EXPECT_EQ(roundtrip_or_err->module_id, label.module_id);
EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id);
EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name);
diff --git a/lldb/unittests/Language/CPlusPlus/CMakeLists.txt b/lldb/unittests/Language/CPlusPlus/CMakeLists.txt
index 4882eafc8d85..1d96fcf3db1b 100644
--- a/lldb/unittests/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/unittests/Language/CPlusPlus/CMakeLists.txt
@@ -3,4 +3,5 @@ add_lldb_unittest(LanguageCPlusPlusTests
LINK_LIBS
lldbPluginCPlusPlusLanguage
+ LLVMTestingSupport
)
diff --git a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
index 957fb3f60049..8c36f540370f 100644
--- a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
+++ b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp
@@ -9,6 +9,8 @@
#include "Plugins/Language/CPlusPlus/CPlusPlusNameParser.h"
#include "TestingSupport/SubsystemRAII.h"
#include "lldb/lldb-enumerations.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
@@ -427,3 +429,176 @@ TEST(CPlusPlusLanguage, MatchesCxx) {
Mangled msvcSymbol("??x@@3AH");
EXPECT_TRUE(CPlusPlusLang->SymbolNameFitsToLanguage(msvcSymbol));
}
+
+struct ManglingSubstitutorTestCase {
+ llvm::StringRef mangled;
+ llvm::StringRef from;
+ llvm::StringRef to;
+ llvm::StringRef expected;
+ bool expect_error;
+};
+
+struct ManglingSubstitutorTestFixture
+ : public ::testing::TestWithParam<ManglingSubstitutorTestCase> {};
+
+ManglingSubstitutorTestCase g_mangled_substitutor_type_test_cases[] = {
+ {/*.mangled*/ "_Z3fooa", /*from*/ "a", /*to*/ "c", /*expected*/ "_Z3fooc",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3fooy", /*from*/ "y", /*to*/ "m", /*expected*/ "_Z3foom",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3foox", /*from*/ "x", /*to*/ "l", /*expected*/ "_Z3fool",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3baraa", /*from*/ "a", /*to*/ "c", /*expected*/ "_Z3barcc",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3foov", /*from*/ "x", /*to*/ "l", /*expected*/ "",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3fooB3Tagv", /*from*/ "Tag", /*to*/ "random",
+ /*expected*/ "", /*expect_error*/ false},
+ {/*.mangled*/ "_Z3foocc", /*from*/ "a", /*to*/ "c", /*expected*/ "",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3fooIaE3barIaEEvaT_", /*from*/ "a", /*to*/ "c",
+ /*expected*/ "_ZN3fooIcE3barIcEEvcT_", /*expect_error*/ false},
+ {/*.mangled*/ "foo", /*from*/ "x", /*to*/ "l", /*expected*/ "",
+ /*expect_error*/ true},
+ {/*.mangled*/ "", /*from*/ "x", /*to*/ "l", /*expected*/ "",
+ /*expect_error*/ true},
+ // FIXME: these two cases are odd behaviours, though not realistic in
+ // practice.
+ {/*.mangled*/ "_Z3foox", /*from*/ "", /*to*/ "l", /*expected*/ "_Z3foolx",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_Z3foox", /*from*/ "x", /*to*/ "", /*expected*/ "_Z3foo",
+ /*expect_error*/ false}};
+
+TEST_P(ManglingSubstitutorTestFixture, Type) {
+ // Tests the CPlusPlusLanguage::SubstituteType_ItaniumMangle API.
+
+ const auto &[mangled, from, to, expected, expect_error] = GetParam();
+
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteType_ItaniumMangle(mangled, from, to);
+ if (expect_error) {
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Failed());
+ } else {
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, expected);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ManglingSubstitutorTypeTests, ManglingSubstitutorTestFixture,
+ ::testing::ValuesIn(g_mangled_substitutor_type_test_cases));
+
+struct ManglingSubstitutorStructorTestFixture
+ : public ::testing::TestWithParam<ManglingSubstitutorTestCase> {};
+
+ManglingSubstitutorTestCase g_mangled_substitutor_structor_test_cases[] = {
+ {/*.mangled*/ "_ZN3FooC1Ev", /*from*/ "C1", /*to*/ "C2",
+ /*expected*/ "_ZN3FooC2Ev", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooC4Ev", /*from*/ "C4", /*to*/ "C2",
+ /*expected*/ "_ZN3FooC2Ev", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooC2Ev", /*from*/ "C1", /*to*/ "C2", /*expected*/ "",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooD1Ev", /*from*/ "D1", /*to*/ "D2",
+ /*expected*/ "_ZN3FooD2Ev", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooD2Ev", /*from*/ "D1", /*to*/ "D2", /*expected*/ "",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooD4Ev", /*from*/ "D4", /*to*/ "D2",
+ /*expected*/ "_ZN3FooD2Ev", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN2D12C1C1I2C12D1EE2C12D1", /*from*/ "C1", /*to*/ "C2",
+ /*expected*/ "_ZN2D12C1C2I2C12D1EE2C12D1", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN2D12C1D1I2C12D1EE2C12D1", /*from*/ "D1", /*to*/ "D2",
+ /*expected*/ "_ZN2D12C1D2I2C12D1EE2C12D1", /*expect_error*/ false},
+ {/*.mangled*/ "_ZN3FooC6Ev", /*from*/ "D1", /*to*/ "D2", /*expected*/ "",
+ /*expect_error*/ true},
+ {/*.mangled*/ "_ZN2D12C1B2D1C1I2C1B2C12D1B2D1EE2C1B2C12D1B2D1",
+ /*from*/ "C1", /*to*/ "C2",
+ /*expected*/ "_ZN2D12C1B2D1C2I2C1B2C12D1B2D1EE2C1B2C12D1B2D1",
+ /*expect_error*/ false},
+ {/*.mangled*/ "_ZN2D12C1B2D1D1I2C1B2C12D1B2D1EE2C1B2C12D1B2D1",
+ /*from*/ "D1", /*to*/ "D2",
+ /*expected*/ "_ZN2D12C1B2D1D2I2C1B2C12D1B2D1EE2C1B2C12D1B2D1",
+ /*expect_error*/ false},
+};
+
+TEST_P(ManglingSubstitutorStructorTestFixture, Structors) {
+ // Tests the CPlusPlusLanguage::SubstituteStructor_ItaniumMangle API.
+
+ const auto &[mangled, from, to, expected, expect_error] = GetParam();
+
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(mangled, from, to);
+ if (expect_error) {
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Failed());
+ } else {
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, expected);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ManglingSubstitutorStructorTests, ManglingSubstitutorStructorTestFixture,
+ ::testing::ValuesIn(g_mangled_substitutor_structor_test_cases));
+
+TEST(CPlusPlusLanguage, ManglingSubstitutor_StructorAlias) {
+ // Tests the CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle API.
+ {
+ // Invalid mangling.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle("Foo");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Failed());
+ }
+
+ {
+ // Ctor C1 alias.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN3FooC1Ev");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, "_ZN3FooC2Ev");
+ }
+
+ {
+ // Dtor D1 alias.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN3FooD1Ev");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, "_ZN3FooD2Ev");
+ }
+
+ {
+ // Ctor C2 not aliased.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN3FooC2Ev");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_FALSE(*subst_or_err);
+ }
+
+ {
+ // Dtor D2 not aliased.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN3FooD2Ev");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_FALSE(*subst_or_err);
+ }
+
+ {
+ // Check that ctor variants in other parts of the name don't get replaced.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN2D12C1B2D1C1I2C1B2C12D1B2D1EE2C1B2C12D1B2D1");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, "_ZN2D12C1B2D1C2I2C1B2C12D1B2D1EE2C1B2C12D1B2D1");
+ }
+
+ {
+ // Check that dtor variants in other parts of the name don't get replaced.
+ auto subst_or_err =
+ CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+ "_ZN2D12C1B2D1D1I2C1B2C12D1B2D1EE2C1B2C12D1B2D1");
+ EXPECT_THAT_EXPECTED(subst_or_err, llvm::Succeeded());
+ EXPECT_EQ(*subst_or_err, "_ZN2D12C1B2D1D2I2C1B2C12D1B2D1EE2C1B2C12D1B2D1");
+ }
+}
diff --git a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
index 80abc5b80f84..411387832f73 100644
--- a/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
+++ b/lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp
@@ -308,3 +308,84 @@ Sections:
auto entry_point_addr = module_sp->GetObjectFile()->GetEntryPointAddress();
ASSERT_EQ(entry_point_addr.GetAddressClass(), AddressClass::eCode);
}
+
+TEST_F(ObjectFileELFTest, SkipsLocalMappingAndDotLSymbols) {
+ auto ExpectedFile = TestFile::fromYaml(R"(
+--- !ELF
+ FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_RISCV
+ Flags: [ EF_RISCV_RVC, EF_RISCV_FLOAT_ABI_SINGLE ]
+ Entry: 0xC0A1B010
+ Sections:
+ - Name: .text
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ Address: 0x0000000000400180
+ AddressAlign: 0x0000000000000010
+ Content: 554889E5
+ - Name: .data
+ Type: SHT_PROGBITS
+ Flags: [ SHF_WRITE, SHF_ALLOC ]
+ Address: 0x0000000000601000
+ AddressAlign: 0x0000000000000004
+ Content: 2F000000
+ - Name: .riscv.attributes
+ Type: SHT_PROGBITS
+ Flags: [ SHF_ALLOC ]
+ Address: 0x0000000000610000
+ AddressAlign: 0x0000000000000004
+ Content: "00"
+ Symbols:
+ - Name: $d
+ Type: STT_NOTYPE
+ Section: .riscv.attributes
+ Value: 0x0000000000400180
+ Size: 0x10
+ Binding: STB_LOCAL
+ - Name: $x
+ Type: STT_NOTYPE
+ Section: .text
+ Value: 0xC0A1B010
+ Size: 0x10
+ Binding: STB_LOCAL
+ - Name: .Lfoo
+ Type: STT_OBJECT
+ Section: .data
+ Value: 0x0000000000601000
+ Size: 0x4
+ Binding: STB_LOCAL
+ - Name: global_func
+ Type: STT_FUNC
+ Section: .text
+ Value: 0x00000000004001A0
+ Size: 0x10
+ Binding: STB_GLOBAL
+ - Name: global_obj
+ Type: STT_OBJECT
+ Section: .data
+ Value: 0x0000000000601004
+ Size: 0x4
+ Binding: STB_GLOBAL
+...
+ )");
+ ASSERT_THAT_EXPECTED(ExpectedFile, llvm::Succeeded());
+ auto module_sp = std::make_shared<Module>(ExpectedFile->moduleSpec());
+ auto *symtab = module_sp->GetSymtab();
+ ASSERT_NE(nullptr, symtab);
+ EXPECT_EQ(nullptr, module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("$d"), eSymbolTypeAny));
+ EXPECT_EQ(nullptr, module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("$x"), eSymbolTypeAny));
+ EXPECT_EQ(nullptr, module_sp->FindFirstSymbolWithNameAndType(
+ ConstString(".Lfoo"), eSymbolTypeAny));
+ // assert that other symbols are present
+ const Symbol *global_func = module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("global_func"), eSymbolTypeAny);
+ ASSERT_NE(nullptr, global_func);
+ const Symbol *global_obj = module_sp->FindFirstSymbolWithNameAndType(
+ ConstString("global_obj"), eSymbolTypeAny);
+ ASSERT_NE(nullptr, global_obj);
+}
diff --git a/lldb/unittests/Protocol/CMakeLists.txt b/lldb/unittests/Protocol/CMakeLists.txt
index bbac69611e01..f877517ea233 100644
--- a/lldb/unittests/Protocol/CMakeLists.txt
+++ b/lldb/unittests/Protocol/CMakeLists.txt
@@ -1,5 +1,6 @@
add_lldb_unittest(ProtocolTests
ProtocolMCPTest.cpp
+ ProtocolMCPServerTest.cpp
LINK_LIBS
lldbHost
diff --git a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
new file mode 100644
index 000000000000..f686255c6d41
--- /dev/null
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -0,0 +1,292 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ProtocolMCPTestUtilities.h"
+#include "TestingSupport/Host/JSONTransportTestUtilities.h"
+#include "TestingSupport/Host/PipeTestUtilities.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Host/Socket.h"
+#include "lldb/Protocol/MCP/MCPError.h"
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "lldb/Protocol/MCP/Resource.h"
+#include "lldb/Protocol/MCP/Server.h"
+#include "lldb/Protocol/MCP/Tool.h"
+#include "lldb/Protocol/MCP/Transport.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <chrono>
+#include <condition_variable>
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_protocol::mcp;
+
+namespace {
+
+class TestServer : public Server {
+public:
+ using Server::Server;
+};
+
+/// Test tool that returns it argument as text.
+class TestTool : public Tool {
+public:
+ using Tool::Tool;
+
+ llvm::Expected<CallToolResult> Call(const ToolArguments &args) override {
+ std::string argument;
+ if (const json::Object *args_obj =
+ std::get<json::Value>(args).getAsObject()) {
+ if (const json::Value *s = args_obj->get("arguments")) {
+ argument = s->getAsString().value_or("");
+ }
+ }
+
+ CallToolResult text_result;
+ text_result.content.emplace_back(TextContent{{argument}});
+ return text_result;
+ }
+};
+
+class TestResourceProvider : public ResourceProvider {
+ using ResourceProvider::ResourceProvider;
+
+ std::vector<Resource> GetResources() const override {
+ std::vector<Resource> resources;
+
+ Resource resource;
+ resource.uri = "lldb://foo/bar";
+ resource.name = "name";
+ resource.description = "description";
+ resource.mimeType = "application/json";
+
+ resources.push_back(resource);
+ return resources;
+ }
+
+ llvm::Expected<ReadResourceResult>
+ ReadResource(llvm::StringRef uri) const override {
+ if (uri != "lldb://foo/bar")
+ return llvm::make_error<UnsupportedURI>(uri.str());
+
+ TextResourceContents contents;
+ contents.uri = "lldb://foo/bar";
+ contents.mimeType = "application/json";
+ contents.text = "foobar";
+
+ ReadResourceResult result;
+ result.contents.push_back(contents);
+ return result;
+ }
+};
+
+/// Test tool that returns an error.
+class ErrorTool : public Tool {
+public:
+ using Tool::Tool;
+
+ llvm::Expected<CallToolResult> Call(const ToolArguments &args) override {
+ return llvm::createStringError("error");
+ }
+};
+
+/// Test tool that fails but doesn't return an error.
+class FailTool : public Tool {
+public:
+ using Tool::Tool;
+
+ llvm::Expected<CallToolResult> Call(const ToolArguments &args) override {
+ CallToolResult text_result;
+ text_result.content.emplace_back(TextContent{{"failed"}});
+ text_result.isError = true;
+ return text_result;
+ }
+};
+
+class ProtocolServerMCPTest : public PipePairTest {
+public:
+ SubsystemRAII<FileSystem, HostInfo, Socket> subsystems;
+
+ std::unique_ptr<lldb_protocol::mcp::Transport> transport_up;
+ std::unique_ptr<TestServer> server_up;
+ MainLoop loop;
+ MockMessageHandler<Request, Response, Notification> message_handler;
+
+ llvm::Error Write(llvm::StringRef message) {
+ llvm::Expected<json::Value> value = json::parse(message);
+ if (!value)
+ return value.takeError();
+ return transport_up->Write(*value);
+ }
+
+ llvm::Error Write(json::Value value) { return transport_up->Write(value); }
+
+ /// Run the transport MainLoop and return any messages received.
+ llvm::Error
+ Run(std::chrono::milliseconds timeout = std::chrono::milliseconds(200)) {
+ loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
+ timeout);
+ auto handle = transport_up->RegisterMessageHandler(loop, message_handler);
+ if (!handle)
+ return handle.takeError();
+
+ return server_up->Run();
+ }
+
+ void SetUp() override {
+ PipePairTest::SetUp();
+
+ transport_up = std::make_unique<lldb_protocol::mcp::Transport>(
+ std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
+ File::eOpenOptionReadOnly,
+ NativeFile::Unowned),
+ std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
+ File::eOpenOptionWriteOnly,
+ NativeFile::Unowned));
+
+ server_up = std::make_unique<TestServer>(
+ "lldb-mcp", "0.1.0",
+ std::make_unique<lldb_protocol::mcp::Transport>(
+ std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
+ File::eOpenOptionReadOnly,
+ NativeFile::Unowned),
+ std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
+ File::eOpenOptionWriteOnly,
+ NativeFile::Unowned)),
+ loop);
+ }
+};
+
+template <typename T>
+Request make_request(StringLiteral method, T &&params, Id id = 1) {
+ return Request{id, method.str(), toJSON(std::forward<T>(params))};
+}
+
+template <typename T> Response make_response(T &&result, Id id = 1) {
+ return Response{id, std::forward<T>(result)};
+}
+
+} // namespace
+
+TEST_F(ProtocolServerMCPTest, Initialization) {
+ Request request = make_request(
+ "initialize", InitializeParams{/*protocolVersion=*/"2024-11-05",
+ /*capabilities=*/{},
+ /*clientInfo=*/{"lldb-unit", "0.1.0"}});
+ Response response = make_response(
+ InitializeResult{/*protocolVersion=*/"2024-11-05",
+ /*capabilities=*/{/*supportsToolsList=*/true},
+ /*serverInfo=*/{"lldb-mcp", "0.1.0"}});
+
+ ASSERT_THAT_ERROR(Write(request), Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, ToolsList) {
+ server_up->AddTool(std::make_unique<TestTool>("test", "test tool"));
+
+ Request request = make_request("tools/list", Void{}, /*id=*/"one");
+
+ ToolDefinition test_tool;
+ test_tool.name = "test";
+ test_tool.description = "test tool";
+ test_tool.inputSchema = json::Object{{"type", "object"}};
+
+ Response response = make_response(ListToolsResult{{test_tool}}, /*id=*/"one");
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, ResourcesList) {
+ server_up->AddResourceProvider(std::make_unique<TestResourceProvider>());
+
+ Request request = make_request("resources/list", Void{});
+ Response response = make_response(ListResourcesResult{
+ {{/*uri=*/"lldb://foo/bar", /*name=*/"name",
+ /*description=*/"description", /*mimeType=*/"application/json"}}});
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, ToolsCall) {
+ server_up->AddTool(std::make_unique<TestTool>("test", "test tool"));
+
+ Request request = make_request(
+ "tools/call", CallToolParams{/*name=*/"test", /*arguments=*/json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ }});
+ Response response = make_response(CallToolResult{{{/*text=*/"foo"}}});
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, ToolsCallError) {
+ server_up->AddTool(std::make_unique<ErrorTool>("error", "error tool"));
+
+ Request request = make_request(
+ "tools/call", CallToolParams{/*name=*/"error", /*arguments=*/json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ }});
+ Response response =
+ make_response(lldb_protocol::mcp::Error{eErrorCodeInternalError,
+ /*message=*/"error"});
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, ToolsCallFail) {
+ server_up->AddTool(std::make_unique<FailTool>("fail", "fail tool"));
+
+ Request request = make_request(
+ "tools/call", CallToolParams{/*name=*/"fail", /*arguments=*/json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ }});
+ Response response =
+ make_response(CallToolResult{{{/*text=*/"failed"}}, /*isError=*/true});
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_CALL(message_handler, Received(response));
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(ProtocolServerMCPTest, NotificationInitialized) {
+ bool handler_called = false;
+ std::condition_variable cv;
+
+ server_up->AddNotificationHandler(
+ "notifications/initialized",
+ [&](const Notification &notification) { handler_called = true; });
+ llvm::StringLiteral request =
+ R"json({"method":"notifications/initialized","jsonrpc":"2.0"})json";
+
+ ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
+ EXPECT_THAT_ERROR(Run(), Succeeded());
+ EXPECT_TRUE(handler_called);
+}
diff --git a/lldb/unittests/Protocol/ProtocolMCPTest.cpp b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
index ea19922522ff..396e361e873f 100644
--- a/lldb/unittests/Protocol/ProtocolMCPTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
@@ -6,6 +6,7 @@
//
//===----------------------------------------------------------------------===//
+#include "ProtocolMCPTestUtilities.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Testing/Support/Error.h"
@@ -54,31 +55,16 @@ TEST(ProtocolMCPTest, Notification) {
EXPECT_EQ(notification.params, deserialized_notification->params);
}
-TEST(ProtocolMCPTest, ToolCapability) {
- ToolCapability tool_capability;
- tool_capability.listChanged = true;
+TEST(ProtocolMCPTest, ServerCapabilities) {
+ ServerCapabilities capabilities;
+ capabilities.supportsToolsList = true;
- llvm::Expected<ToolCapability> deserialized_tool_capability =
- roundtripJSON(tool_capability);
- ASSERT_THAT_EXPECTED(deserialized_tool_capability, llvm::Succeeded());
-
- EXPECT_EQ(tool_capability.listChanged,
- deserialized_tool_capability->listChanged);
-}
-
-TEST(ProtocolMCPTest, Capabilities) {
- ToolCapability tool_capability;
- tool_capability.listChanged = true;
-
- Capabilities capabilities;
- capabilities.tools = tool_capability;
-
- llvm::Expected<Capabilities> deserialized_capabilities =
+ llvm::Expected<ServerCapabilities> deserialized_capabilities =
roundtripJSON(capabilities);
ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded());
- EXPECT_EQ(capabilities.tools.listChanged,
- deserialized_capabilities->tools.listChanged);
+ EXPECT_EQ(capabilities.supportsToolsList,
+ deserialized_capabilities->supportsToolsList);
}
TEST(ProtocolMCPTest, TextContent) {
@@ -92,18 +78,18 @@ TEST(ProtocolMCPTest, TextContent) {
EXPECT_EQ(text_content.text, deserialized_text_content->text);
}
-TEST(ProtocolMCPTest, TextResult) {
+TEST(ProtocolMCPTest, CallToolResult) {
TextContent text_content1;
text_content1.text = "Text 1";
TextContent text_content2;
text_content2.text = "Text 2";
- TextResult text_result;
+ CallToolResult text_result;
text_result.content = {text_content1, text_content2};
text_result.isError = true;
- llvm::Expected<TextResult> deserialized_text_result =
+ llvm::Expected<CallToolResult> deserialized_text_result =
roundtripJSON(text_result);
ASSERT_THAT_EXPECTED(deserialized_text_result, llvm::Succeeded());
@@ -237,13 +223,13 @@ TEST(ProtocolMCPTest, ResourceWithoutOptionals) {
EXPECT_TRUE(deserialized_resource->mimeType.empty());
}
-TEST(ProtocolMCPTest, ResourceContents) {
- ResourceContents contents;
+TEST(ProtocolMCPTest, TextResourceContents) {
+ TextResourceContents contents;
contents.uri = "resource://example/content";
contents.text = "This is the content of the resource";
contents.mimeType = "text/plain";
- llvm::Expected<ResourceContents> deserialized_contents =
+ llvm::Expected<TextResourceContents> deserialized_contents =
roundtripJSON(contents);
ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
@@ -252,12 +238,12 @@ TEST(ProtocolMCPTest, ResourceContents) {
EXPECT_EQ(contents.mimeType, deserialized_contents->mimeType);
}
-TEST(ProtocolMCPTest, ResourceContentsWithoutMimeType) {
- ResourceContents contents;
+TEST(ProtocolMCPTest, TextResourceContentsWithoutMimeType) {
+ TextResourceContents contents;
contents.uri = "resource://example/content-no-mime";
contents.text = "Content without mime type specified";
- llvm::Expected<ResourceContents> deserialized_contents =
+ llvm::Expected<TextResourceContents> deserialized_contents =
roundtripJSON(contents);
ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
@@ -266,21 +252,22 @@ TEST(ProtocolMCPTest, ResourceContentsWithoutMimeType) {
EXPECT_TRUE(deserialized_contents->mimeType.empty());
}
-TEST(ProtocolMCPTest, ResourceResult) {
- ResourceContents contents1;
+TEST(ProtocolMCPTest, ReadResourceResult) {
+ TextResourceContents contents1;
contents1.uri = "resource://example/content1";
contents1.text = "First resource content";
contents1.mimeType = "text/plain";
- ResourceContents contents2;
+ TextResourceContents contents2;
contents2.uri = "resource://example/content2";
contents2.text = "Second resource content";
contents2.mimeType = "application/json";
- ResourceResult result;
+ ReadResourceResult result;
result.contents = {contents1, contents2};
- llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(result);
+ llvm::Expected<ReadResourceResult> deserialized_result =
+ roundtripJSON(result);
ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
ASSERT_EQ(result.contents.size(), deserialized_result->contents.size());
@@ -296,10 +283,11 @@ TEST(ProtocolMCPTest, ResourceResult) {
deserialized_result->contents[1].mimeType);
}
-TEST(ProtocolMCPTest, ResourceResultEmpty) {
- ResourceResult result;
+TEST(ProtocolMCPTest, ReadResourceResultEmpty) {
+ ReadResourceResult result;
- llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(result);
+ llvm::Expected<ReadResourceResult> deserialized_result =
+ roundtripJSON(result);
ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
EXPECT_TRUE(deserialized_result->contents.empty());
diff --git a/lldb/unittests/Protocol/ProtocolMCPTestUtilities.h b/lldb/unittests/Protocol/ProtocolMCPTestUtilities.h
new file mode 100644
index 000000000000..f8a14f4be03c
--- /dev/null
+++ b/lldb/unittests/Protocol/ProtocolMCPTestUtilities.h
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UNITTESTS_PROTOCOL_PROTOCOLMCPTESTUTILITIES_H
+#define LLDB_UNITTESTS_PROTOCOL_PROTOCOLMCPTESTUTILITIES_H
+
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h" // IWYU pragma: keep
+#include "gtest/gtest.h" // IWYU pragma: keep
+#include <ostream>
+#include <variant>
+
+namespace lldb_protocol::mcp {
+
+inline void PrintTo(const Request &req, std::ostream *os) {
+ *os << llvm::formatv("{0}", toJSON(req)).str();
+}
+
+inline void PrintTo(const Response &resp, std::ostream *os) {
+ *os << llvm::formatv("{0}", toJSON(resp)).str();
+}
+
+inline void PrintTo(const Notification &note, std::ostream *os) {
+ *os << llvm::formatv("{0}", toJSON(note)).str();
+}
+
+inline void PrintTo(const Message &message, std::ostream *os) {
+ return std::visit([os](auto &&message) { return PrintTo(message, os); },
+ message);
+}
+
+} // namespace lldb_protocol::mcp
+
+#endif
diff --git a/lldb/unittests/ProtocolServer/CMakeLists.txt b/lldb/unittests/ProtocolServer/CMakeLists.txt
deleted file mode 100644
index 6117430b35bf..000000000000
--- a/lldb/unittests/ProtocolServer/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-add_lldb_unittest(ProtocolServerTests
- ProtocolMCPServerTest.cpp
-
- LINK_LIBS
- lldbCore
- lldbUtility
- lldbHost
- lldbPluginPlatformMacOSX
- lldbPluginProtocolServerMCP
- LLVMTestingSupport
- )
diff --git a/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp b/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
deleted file mode 100644
index 18112428950c..000000000000
--- a/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
+++ /dev/null
@@ -1,333 +0,0 @@
-//===-- ProtocolServerMCPTest.cpp -----------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"
-#include "Plugins/Protocol/MCP/ProtocolServerMCP.h"
-#include "TestingSupport/Host/JSONTransportTestUtilities.h"
-#include "TestingSupport/SubsystemRAII.h"
-#include "lldb/Core/Debugger.h"
-#include "lldb/Core/ProtocolServer.h"
-#include "lldb/Host/FileSystem.h"
-#include "lldb/Host/HostInfo.h"
-#include "lldb/Host/JSONTransport.h"
-#include "lldb/Host/MainLoop.h"
-#include "lldb/Host/MainLoopBase.h"
-#include "lldb/Host/Socket.h"
-#include "lldb/Host/common/TCPSocket.h"
-#include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Protocol/MCP/Protocol.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Testing/Support/Error.h"
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-using namespace llvm;
-using namespace lldb;
-using namespace lldb_private;
-using namespace lldb_protocol::mcp;
-using testing::_;
-
-namespace {
-class TestProtocolServerMCP : public lldb_private::mcp::ProtocolServerMCP {
-public:
- using ProtocolServerMCP::AddNotificationHandler;
- using ProtocolServerMCP::AddRequestHandler;
- using ProtocolServerMCP::AddResourceProvider;
- using ProtocolServerMCP::AddTool;
- using ProtocolServerMCP::GetSocket;
- using ProtocolServerMCP::ProtocolServerMCP;
-};
-
-using Message = typename Transport<Request, Response, Notification>::Message;
-
-class TestJSONTransport final
- : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
-public:
- using JSONRPCTransport::JSONRPCTransport;
-
- void Log(llvm::StringRef message) override {
- log_messages.emplace_back(message);
- }
-
- std::vector<std::string> log_messages;
-};
-
-/// Test tool that returns it argument as text.
-class TestTool : public Tool {
-public:
- using Tool::Tool;
-
- llvm::Expected<TextResult> Call(const ToolArguments &args) override {
- std::string argument;
- if (const json::Object *args_obj =
- std::get<json::Value>(args).getAsObject()) {
- if (const json::Value *s = args_obj->get("arguments")) {
- argument = s->getAsString().value_or("");
- }
- }
-
- TextResult text_result;
- text_result.content.emplace_back(TextContent{{argument}});
- return text_result;
- }
-};
-
-class TestResourceProvider : public ResourceProvider {
- using ResourceProvider::ResourceProvider;
-
- std::vector<Resource> GetResources() const override {
- std::vector<Resource> resources;
-
- Resource resource;
- resource.uri = "lldb://foo/bar";
- resource.name = "name";
- resource.description = "description";
- resource.mimeType = "application/json";
-
- resources.push_back(resource);
- return resources;
- }
-
- llvm::Expected<ResourceResult>
- ReadResource(llvm::StringRef uri) const override {
- if (uri != "lldb://foo/bar")
- return llvm::make_error<UnsupportedURI>(uri.str());
-
- ResourceContents contents;
- contents.uri = "lldb://foo/bar";
- contents.mimeType = "application/json";
- contents.text = "foobar";
-
- ResourceResult result;
- result.contents.push_back(contents);
- return result;
- }
-};
-
-/// Test tool that returns an error.
-class ErrorTool : public Tool {
-public:
- using Tool::Tool;
-
- llvm::Expected<TextResult> Call(const ToolArguments &args) override {
- return llvm::createStringError("error");
- }
-};
-
-/// Test tool that fails but doesn't return an error.
-class FailTool : public Tool {
-public:
- using Tool::Tool;
-
- llvm::Expected<TextResult> Call(const ToolArguments &args) override {
- TextResult text_result;
- text_result.content.emplace_back(TextContent{{"failed"}});
- text_result.isError = true;
- return text_result;
- }
-};
-
-class ProtocolServerMCPTest : public ::testing::Test {
-public:
- SubsystemRAII<FileSystem, HostInfo, PlatformRemoteMacOSX, Socket> subsystems;
- DebuggerSP m_debugger_sp;
-
- lldb::IOObjectSP m_io_sp;
- std::unique_ptr<TestJSONTransport> m_transport_up;
- std::unique_ptr<TestProtocolServerMCP> m_server_up;
- MainLoop loop;
- MockMessageHandler<Request, Response, Notification> message_handler;
-
- static constexpr llvm::StringLiteral k_localhost = "localhost";
-
- llvm::Error Write(llvm::StringRef message) {
- std::string output = llvm::formatv("{0}\n", message).str();
- size_t bytes_written = output.size();
- return m_io_sp->Write(output.data(), bytes_written).takeError();
- }
-
- void CloseInput() {
- EXPECT_THAT_ERROR(m_io_sp->Close().takeError(), Succeeded());
- }
-
- /// Run the transport MainLoop and return any messages received.
- llvm::Error
- Run(std::chrono::milliseconds timeout = std::chrono::milliseconds(200)) {
- loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
- timeout);
- auto handle = m_transport_up->RegisterMessageHandler(loop, message_handler);
- if (!handle)
- return handle.takeError();
-
- return loop.Run().takeError();
- }
-
- void SetUp() override {
- // Create a debugger.
- ArchSpec arch("arm64-apple-macosx-");
- Platform::SetHostPlatform(
- PlatformRemoteMacOSX::CreateInstance(true, &arch));
- m_debugger_sp = Debugger::CreateInstance();
-
- // Create & start the server.
- ProtocolServer::Connection connection;
- connection.protocol = Socket::SocketProtocol::ProtocolTcp;
- connection.name = llvm::formatv("{0}:0", k_localhost).str();
- m_server_up = std::make_unique<TestProtocolServerMCP>();
- m_server_up->AddTool(std::make_unique<TestTool>("test", "test tool"));
- m_server_up->AddResourceProvider(std::make_unique<TestResourceProvider>());
- ASSERT_THAT_ERROR(m_server_up->Start(connection), llvm::Succeeded());
-
- // Connect to the server over a TCP socket.
- auto connect_socket_up = std::make_unique<TCPSocket>(true);
- ASSERT_THAT_ERROR(connect_socket_up
- ->Connect(llvm::formatv("{0}:{1}", k_localhost,
- static_cast<TCPSocket *>(
- m_server_up->GetSocket())
- ->GetLocalPortNumber())
- .str())
- .ToError(),
- llvm::Succeeded());
-
- // Set up JSON transport for the client.
- m_io_sp = std::move(connect_socket_up);
- m_transport_up = std::make_unique<TestJSONTransport>(m_io_sp, m_io_sp);
- }
-
- void TearDown() override {
- // Stop the server.
- ASSERT_THAT_ERROR(m_server_up->Stop(), llvm::Succeeded());
- }
-};
-
-} // namespace
-
-TEST_F(ProtocolServerMCPTest, Initialization) {
- llvm::StringLiteral request =
- R"json({"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"lldb-unit","version":"0.1.0"}},"jsonrpc":"2.0","id":1})json";
- llvm::StringLiteral response =
- R"json({"id":1,"jsonrpc":"2.0","result":{"capabilities":{"resources":{"listChanged":false,"subscribe":false},"tools":{"listChanged":true}},"protocolVersion":"2024-11-05","serverInfo":{"name":"lldb-mcp","version":"0.1.0"}}})json";
-
- ASSERT_THAT_ERROR(Write(request), Succeeded());
- llvm::Expected<Response> expected_resp = json::parse<Response>(response);
- ASSERT_THAT_EXPECTED(expected_resp, llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(*expected_resp));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, ToolsList) {
- llvm::StringLiteral request =
- R"json({"method":"tools/list","params":{},"jsonrpc":"2.0","id":"one"})json";
-
- ToolDefinition test_tool;
- test_tool.name = "test";
- test_tool.description = "test tool";
- test_tool.inputSchema = json::Object{{"type", "object"}};
-
- ToolDefinition lldb_command_tool;
- lldb_command_tool.description = "Run an lldb command.";
- lldb_command_tool.name = "lldb_command";
- lldb_command_tool.inputSchema = json::Object{
- {"type", "object"},
- {"properties",
- json::Object{{"arguments", json::Object{{"type", "string"}}},
- {"debugger_id", json::Object{{"type", "number"}}}}},
- {"required", json::Array{"debugger_id"}}};
- Response response;
- response.id = "one";
- response.result = json::Object{
- {"tools",
- json::Array{std::move(test_tool), std::move(lldb_command_tool)}},
- };
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(response));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, ResourcesList) {
- llvm::StringLiteral request =
- R"json({"method":"resources/list","params":{},"jsonrpc":"2.0","id":2})json";
- llvm::StringLiteral response =
- R"json({"id":2,"jsonrpc":"2.0","result":{"resources":[{"description":"description","mimeType":"application/json","name":"name","uri":"lldb://foo/bar"}]}})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
- llvm::Expected<Response> expected_resp = json::parse<Response>(response);
- ASSERT_THAT_EXPECTED(expected_resp, llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(*expected_resp));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, ToolsCall) {
- llvm::StringLiteral request =
- R"json({"method":"tools/call","params":{"name":"test","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
- llvm::StringLiteral response =
- R"json({"id":11,"jsonrpc":"2.0","result":{"content":[{"text":"foo","type":"text"}],"isError":false}})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
- llvm::Expected<Response> expected_resp = json::parse<Response>(response);
- ASSERT_THAT_EXPECTED(expected_resp, llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(*expected_resp));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, ToolsCallError) {
- m_server_up->AddTool(std::make_unique<ErrorTool>("error", "error tool"));
-
- llvm::StringLiteral request =
- R"json({"method":"tools/call","params":{"name":"error","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
- llvm::StringLiteral response =
- R"json({"error":{"code":-32603,"message":"error"},"id":11,"jsonrpc":"2.0"})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
- llvm::Expected<Response> expected_resp = json::parse<Response>(response);
- ASSERT_THAT_EXPECTED(expected_resp, llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(*expected_resp));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, ToolsCallFail) {
- m_server_up->AddTool(std::make_unique<FailTool>("fail", "fail tool"));
-
- llvm::StringLiteral request =
- R"json({"method":"tools/call","params":{"name":"fail","arguments":{"arguments":"foo","debugger_id":0}},"jsonrpc":"2.0","id":11})json";
- llvm::StringLiteral response =
- R"json({"id":11,"jsonrpc":"2.0","result":{"content":[{"text":"failed","type":"text"}],"isError":true}})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
- llvm::Expected<Response> expected_resp = json::parse<Response>(response);
- ASSERT_THAT_EXPECTED(expected_resp, llvm::Succeeded());
- EXPECT_CALL(message_handler, Received(*expected_resp));
- EXPECT_THAT_ERROR(Run(), Succeeded());
-}
-
-TEST_F(ProtocolServerMCPTest, NotificationInitialized) {
- bool handler_called = false;
- std::condition_variable cv;
- std::mutex mutex;
-
- m_server_up->AddNotificationHandler(
- "notifications/initialized", [&](const Notification &notification) {
- {
- std::lock_guard<std::mutex> lock(mutex);
- handler_called = true;
- }
- cv.notify_all();
- });
- llvm::StringLiteral request =
- R"json({"method":"notifications/initialized","jsonrpc":"2.0"})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
-
- std::unique_lock<std::mutex> lock(mutex);
- cv.wait(lock, [&] { return handler_called; });
-}
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index b993b8261249..f673cceae00d 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1150,12 +1150,12 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) {
is_explicit, is_attr_used, is_artificial);
auto *ctor = m_ast->AddMethodToCXXRecordType(
- t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func:0x0:0x0:S",
+ t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func::0x0:0x0:S",
function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static,
is_inline, is_explicit, is_attr_used, is_artificial);
auto *dtor = m_ast->AddMethodToCXXRecordType(
- t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func:0x0:0x0:~S",
+ t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func::0x0:0x0:~S",
function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static,
is_inline, is_explicit, is_attr_used, is_artificial);
@@ -1181,11 +1181,11 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) {
EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
m_ast->DeclGetMangledName(ctor).GetStringRef())
.data(),
- "$__lldb_func:0x0:0x0:S");
+ "$__lldb_func:C0:0x0:0x0:S");
EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
m_ast->DeclGetMangledName(dtor).GetStringRef())
.data(),
- "$__lldb_func:0x0:0x0:~S");
+ "$__lldb_func:D1:0x0:0x0:~S");
}
struct AsmLabelTestCase {
@@ -1215,10 +1215,10 @@ protected:
};
static AsmLabelTestCase g_asm_label_test_cases[] = {
- {/*mangled=*/"$__lldb_func:0x0:0x0:_Z3foov",
+ {/*mangled=*/"$__lldb_func::0x0:0x0:_Z3foov",
/*expected=*/"_Z3foov"},
- {/*mangled=*/"$__lldb_func:0x0:0x0:foo",
- /*expected=*/"$__lldb_func:0x0:0x0:foo"},
+ {/*mangled=*/"$__lldb_func::0x0:0x0:foo",
+ /*expected=*/"$__lldb_func::0x0:0x0:foo"},
{/*mangled=*/"foo",
/*expected=*/"foo"},
{/*mangled=*/"_Z3foov",
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
index f65034c1dbea..5c78b82f822c 100644
--- a/lldb/unittests/ValueObject/DILLexerTests.cpp
+++ b/lldb/unittests/ValueObject/DILLexerTests.cpp
@@ -151,22 +151,32 @@ TEST(DILLexerTests, IdentifiersTest) {
Token token = lexer.GetCurrentToken();
EXPECT_TRUE(token.IsNot(Token::identifier));
EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren,
- Token::r_paren, Token::numeric_constant}));
+ Token::r_paren, Token::integer_constant}));
}
}
TEST(DILLexerTests, NumbersTest) {
// These strings should lex into number tokens.
- std::vector<std::string> valid_numbers = {"123", "0x123", "0123", "0b101"};
+ std::vector<std::string> valid_integers = {"123", "0x123", "0123", "0b101"};
+ std::vector<std::string> valid_floats = {
+ "1.2", ".2", "2.f", "0x1.2", "0x.2", ".2e1f",
+ "2.e+1f", "0x1.f", "0x1.2P1", "0x1.p-1f", "0x1.2P+3f", "1E1",
+ "1E+1", "0x1p1", "0x1p+1", "0xf.fp1f"};
// The lexer can lex these strings, but they should not be numbers.
- std::vector<std::string> invalid_numbers = {"", "x123", "b123"};
+ std::vector<std::string> invalid_numbers = {"", "x123", "b123", "a.b"};
- for (auto &str : valid_numbers) {
+ for (auto &str : valid_integers) {
SCOPED_TRACE(str);
EXPECT_THAT_EXPECTED(ExtractTokenData(str),
llvm::HasValue(testing::ElementsAre(
- testing::Pair(Token::numeric_constant, str))));
+ testing::Pair(Token::integer_constant, str))));
+ }
+ for (auto &str : valid_floats) {
+ SCOPED_TRACE(str);
+ EXPECT_THAT_EXPECTED(ExtractTokenData(str),
+ llvm::HasValue(testing::ElementsAre(
+ testing::Pair(Token::float_constant, str))));
}
// Verify that none of the invalid numbers come out as numeric tokens.
for (auto &str : invalid_numbers) {
@@ -175,7 +185,27 @@ TEST(DILLexerTests, NumbersTest) {
EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded());
DILLexer lexer(*maybe_lexer);
Token token = lexer.GetCurrentToken();
- EXPECT_TRUE(token.IsNot(Token::numeric_constant));
+ EXPECT_TRUE(token.IsNot(Token::integer_constant));
EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier}));
}
+
+ // Verify that '-' and '+' are not lexed if they're not part of a number
+ std::vector<std::string> expressions = {"1+e", "0x1+p", "1.1+e",
+ "1.1e1+e", "0x1.1p-1-p", "1e-1+e",
+ "1e1+e", "0x1p-1-p", "0xe+e"};
+ for (auto &str : expressions) {
+ SCOPED_TRACE(str);
+ llvm::Expected<DILLexer> maybe_lexer = DILLexer::Create(str);
+ EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded());
+ DILLexer lexer(*maybe_lexer);
+ Token token = lexer.GetCurrentToken();
+ EXPECT_TRUE(
+ token.IsOneOf({Token::integer_constant, Token::float_constant}));
+ lexer.Advance();
+ token = lexer.GetCurrentToken();
+ EXPECT_TRUE(token.IsOneOf({Token::plus, Token::minus}));
+ lexer.Advance();
+ token = lexer.GetCurrentToken();
+ EXPECT_TRUE(token.Is(Token::identifier));
+ }
}
diff --git a/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp b/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
index 2507910d8a97..e6d5b7bb52a2 100644
--- a/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
+++ b/lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
@@ -23,6 +23,33 @@ using namespace llvm;
using namespace lldb_private;
namespace {
+/// Parses curly braces and replaces them with ANSI underline formatting.
+std::string underline(llvm::StringRef Str) {
+ llvm::StringRef OpeningHead, OpeningTail, ClosingHead, ClosingTail;
+ std::string Result;
+ llvm::raw_string_ostream Stream(Result);
+ while (!Str.empty()) {
+ // Find the opening brace.
+ std::tie(OpeningHead, OpeningTail) = Str.split("${");
+ Stream << OpeningHead;
+
+ // No opening brace: we're done.
+ if (OpeningHead == Str)
+ break;
+
+ assert(!OpeningTail.empty());
+
+ // Find the closing brace.
+ std::tie(ClosingHead, ClosingTail) = OpeningTail.split('}');
+ assert(!ClosingTail.empty() &&
+ "unmatched curly braces in command option description");
+
+ Stream << "${ansi.underline}" << ClosingHead << "${ansi.normal}";
+ Str = ClosingTail;
+ }
+ return Result;
+}
+
struct CommandOption {
std::vector<std::string> GroupsArg;
bool Required = false;
@@ -68,7 +95,7 @@ struct CommandOption {
Completions = Option->getValueAsListOfStrings("Completions");
if (auto D = Option->getValue("Description"))
- Description = D->getValue()->getAsUnquotedString();
+ Description = underline(D->getValue()->getAsUnquotedString());
}
};
} // namespace