aboutsummaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
Diffstat (limited to 'lldb')
-rw-r--r--lldb/cmake/caches/Apple-lldb-Xcode.cmake2
-rw-r--r--lldb/cmake/modules/LLDBConfig.cmake10
-rw-r--r--lldb/docs/dil-expr-lang.ebnf6
-rw-r--r--lldb/docs/resources/lldbdap.md23
-rw-r--r--lldb/docs/use/map.rst14
-rw-r--r--lldb/docs/use/remote.rst18
-rwxr-xr-xlldb/examples/python/crashlog.py11
-rw-r--r--lldb/examples/python/crashlog_scripted_process.py20
-rw-r--r--lldb/include/lldb/API/SBError.h1
-rw-r--r--lldb/include/lldb/API/SBFrame.h4
-rw-r--r--lldb/include/lldb/API/SBFunction.h2
-rw-r--r--lldb/include/lldb/API/SBModule.h5
-rw-r--r--lldb/include/lldb/API/SBStructuredData.h29
-rw-r--r--lldb/include/lldb/API/SBSymbol.h17
-rw-r--r--lldb/include/lldb/API/SBTarget.h18
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointResolver.h9
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointResolverName.h2
-rw-r--r--lldb/include/lldb/Core/Disassembler.h40
-rw-r--r--lldb/include/lldb/Core/Mangled.h11
-rw-r--r--lldb/include/lldb/Core/ProtocolServer.h2
-rw-r--r--lldb/include/lldb/Core/StructuredDataImpl.h35
-rw-r--r--lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h7
-rw-r--r--lldb/include/lldb/DataFormatters/ValueObjectPrinter.h6
-rw-r--r--lldb/include/lldb/Expression/DWARFExpression.h3
-rw-r--r--lldb/include/lldb/Host/HostInfoBase.h14
-rw-r--r--lldb/include/lldb/Host/JSONTransport.h309
-rw-r--r--lldb/include/lldb/Host/ProcessRunLock.h13
-rw-r--r--lldb/include/lldb/Host/macosx/HostInfoMacOSX.h3
-rw-r--r--lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h4
-rw-r--r--lldb/include/lldb/Interpreter/OptionValue.h16
-rw-r--r--lldb/include/lldb/Interpreter/OptionValueEnumeration.h1
-rw-r--r--lldb/include/lldb/Interpreter/Options.h4
-rw-r--r--lldb/include/lldb/Protocol/MCP/MCPError.h2
-rw-r--r--lldb/include/lldb/Protocol/MCP/Protocol.h295
-rw-r--r--lldb/include/lldb/Protocol/MCP/Resource.h2
-rw-r--r--lldb/include/lldb/Protocol/MCP/Server.h116
-rw-r--r--lldb/include/lldb/Protocol/MCP/Tool.h3
-rw-r--r--lldb/include/lldb/Symbol/Symbol.h7
-rw-r--r--lldb/include/lldb/Target/CoreFileMemoryRanges.h2
-rw-r--r--lldb/include/lldb/Target/ExecutionContext.h56
-rw-r--r--lldb/include/lldb/Target/StackFrame.h8
-rw-r--r--lldb/include/lldb/Target/StackID.h10
-rw-r--r--lldb/include/lldb/Target/Target.h11
-rw-r--r--lldb/include/lldb/Utility/ArchSpec.h1
-rw-r--r--lldb/include/lldb/Utility/Scalar.h6
-rw-r--r--lldb/include/lldb/Utility/StructuredData.h26
-rw-r--r--lldb/include/lldb/Utility/XcodeSDK.h2
-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/ValueObject/ValueObject.h6
-rw-r--r--lldb/include/lldb/ValueObject/ValueObjectConstResult.h11
-rw-r--r--lldb/include/lldb/lldb-enumerations.h1
-rw-r--r--lldb/include/lldb/lldb-private-enumerations.h16
-rw-r--r--lldb/packages/Python/lldbsuite/test/lldbutil.py7
-rw-r--r--lldb/packages/Python/lldbsuite/test/make/libcxx-simulators-common/compressed_pair.h119
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py810
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py137
-rwxr-xr-xlldb/scripts/framework-header-fix.sh11
-rw-r--r--lldb/source/API/SBFrame.cpp900
-rw-r--r--lldb/source/API/SBFunction.cpp9
-rw-r--r--lldb/source/API/SBHostOS.cpp10
-rw-r--r--lldb/source/API/SBModule.cpp8
-rw-r--r--lldb/source/API/SBStructuredData.cpp44
-rw-r--r--lldb/source/API/SBSymbol.cpp37
-rw-r--r--lldb/source/API/SBTarget.cpp58
-rw-r--r--lldb/source/API/SBThread.cpp577
-rw-r--r--lldb/source/Breakpoint/BreakpointResolver.cpp39
-rw-r--r--lldb/source/Breakpoint/BreakpointResolverAddress.cpp5
-rw-r--r--lldb/source/Breakpoint/BreakpointResolverName.cpp15
-rw-r--r--lldb/source/Commands/CommandObjectDWIMPrint.cpp49
-rw-r--r--lldb/source/Commands/CommandObjectDisassemble.cpp11
-rw-r--r--lldb/source/Commands/CommandObjectDisassemble.h1
-rw-r--r--lldb/source/Commands/CommandObjectExpression.cpp8
-rw-r--r--lldb/source/Commands/CommandObjectFrame.cpp3
-rw-r--r--lldb/source/Commands/CommandObjectProtocolServer.cpp15
-rw-r--r--lldb/source/Commands/CommandObjectSettings.cpp47
-rw-r--r--lldb/source/Commands/CommandObjectTarget.cpp3
-rw-r--r--lldb/source/Commands/Options.td140
-rw-r--r--lldb/source/Core/DemangledNameInfo.cpp11
-rw-r--r--lldb/source/Core/Disassembler.cpp166
-rw-r--r--lldb/source/Core/DynamicLoader.cpp2
-rw-r--r--lldb/source/Core/Mangled.cpp18
-rw-r--r--lldb/source/Core/ModuleList.cpp224
-rw-r--r--lldb/source/Core/ProtocolServer.cpp34
-rw-r--r--lldb/source/Core/Section.cpp3
-rw-r--r--lldb/source/Core/Value.cpp21
-rw-r--r--lldb/source/DataFormatters/DumpValueObjectOptions.cpp17
-rw-r--r--lldb/source/DataFormatters/ValueObjectPrinter.cpp100
-rw-r--r--lldb/source/Expression/DWARFExpression.cpp8
-rw-r--r--lldb/source/Expression/IRExecutionUnit.cpp7
-rw-r--r--lldb/source/Expression/Materializer.cpp25
-rw-r--r--lldb/source/Expression/REPL.cpp2
-rw-r--r--lldb/source/Host/CMakeLists.txt2
-rw-r--r--lldb/source/Host/common/Editline.cpp15
-rw-r--r--lldb/source/Host/common/HostInfoBase.cpp38
-rw-r--r--lldb/source/Host/common/JSONTransport.cpp163
-rw-r--r--lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm12
-rw-r--r--lldb/source/Host/windows/Host.cpp30
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp54
-rw-r--r--lldb/source/Interpreter/CommandObject.cpp6
-rw-r--r--lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp13
-rw-r--r--lldb/source/Interpreter/OptionValueArch.cpp7
-rw-r--r--lldb/source/Interpreter/OptionValueArray.cpp12
-rw-r--r--lldb/source/Interpreter/OptionValueBoolean.cpp6
-rw-r--r--lldb/source/Interpreter/OptionValueChar.cpp18
-rw-r--r--lldb/source/Interpreter/OptionValueDictionary.cpp8
-rw-r--r--lldb/source/Interpreter/OptionValueEnumeration.cpp24
-rw-r--r--lldb/source/Interpreter/OptionValueFileSpec.cpp8
-rw-r--r--lldb/source/Interpreter/OptionValueFileSpecList.cpp13
-rw-r--r--lldb/source/Interpreter/OptionValueFormat.cpp6
-rw-r--r--lldb/source/Interpreter/OptionValueFormatEntity.cpp20
-rw-r--r--lldb/source/Interpreter/OptionValueLanguage.cpp9
-rw-r--r--lldb/source/Interpreter/OptionValuePathMappings.cpp12
-rw-r--r--lldb/source/Interpreter/OptionValueRegex.cpp7
-rw-r--r--lldb/source/Interpreter/OptionValueSInt64.cpp6
-rw-r--r--lldb/source/Interpreter/OptionValueString.cpp40
-rw-r--r--lldb/source/Interpreter/OptionValueUInt64.cpp6
-rw-r--r--lldb/source/Interpreter/Options.cpp13
-rw-r--r--lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp3
-rw-r--r--lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp7
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp152
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h4
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp5
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp4
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp6
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp2
-rw-r--r--lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp4
-rw-r--r--lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp6
-rw-r--r--lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp6
-rw-r--r--lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp2
-rw-r--r--lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp19
-rw-r--r--lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h8
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp351
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h74
-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.cpp12
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp7
-rw-r--r--lldb/source/Plugins/Language/ObjC/NSDictionary.cpp3
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp2
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp2
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp3
-rw-r--r--lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp25
-rw-r--r--lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp41
-rw-r--r--lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp123
-rw-r--r--lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp53
-rw-r--r--lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp451
-rw-r--r--lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h13
-rw-r--r--lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp13
-rw-r--r--lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt2
-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/HistoryThread.cpp5
-rw-r--r--lldb/source/Plugins/Process/Utility/HistoryThread.h6
-rw-r--r--lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp21
-rw-r--r--lldb/source/Plugins/Process/Utility/HistoryUnwind.h6
-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.h4
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp2
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp392
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h67
-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/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp4
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp47
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp46
-rw-r--r--lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp4
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp2
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp9
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp61
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h7
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp11
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp235
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h24
-rw-r--r--lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp11
-rw-r--r--lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp6
-rw-r--r--lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp249
-rw-r--r--lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h9
-rw-r--r--lldb/source/Protocol/MCP/CMakeLists.txt2
-rw-r--r--lldb/source/Protocol/MCP/MCPError.cpp6
-rw-r--r--lldb/source/Protocol/MCP/Protocol.cpp335
-rw-r--r--lldb/source/Protocol/MCP/Server.cpp264
-rw-r--r--lldb/source/Symbol/ObjectFile.cpp1
-rw-r--r--lldb/source/Symbol/Symbol.cpp144
-rw-r--r--lldb/source/Target/ExecutionContext.cpp54
-rw-r--r--lldb/source/Target/RegisterContextUnwind.cpp1
-rw-r--r--lldb/source/Target/StackFrame.cpp42
-rw-r--r--lldb/source/Target/StackFrameRecognizer.cpp2
-rw-r--r--lldb/source/Target/StackID.cpp18
-rw-r--r--lldb/source/Target/Target.cpp56
-rw-r--r--lldb/source/Utility/ArchSpec.cpp4
-rw-r--r--lldb/source/Utility/Scalar.cpp60
-rw-r--r--lldb/source/Utility/XcodeSDK.cpp23
-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/source/ValueObject/ValueObject.cpp7
-rw-r--r--lldb/source/ValueObject/ValueObjectConstResult.cpp27
-rw-r--r--lldb/test/API/arm/thumb-function-addr/Makefile3
-rw-r--r--lldb/test/API/arm/thumb-function-addr/TestThumbFunctionAddr.py67
-rw-r--r--lldb/test/API/arm/thumb-function-addr/main.c9
-rw-r--r--lldb/test/API/commands/expression/TestRegisterExpressionEndian.py128
-rw-r--r--lldb/test/API/commands/expression/import-std-module/queue/TestQueueFromStdModule.py4
-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/settings/TestSettings.py117
-rw-r--r--lldb/test/API/functionalities/asan/TestMemoryHistory.py14
-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.py3
-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/process_crash_info/TestProcessCrashInfo.py25
-rw-r--r--lldb/test/API/functionalities/statusline/TestStatusline.py1
-rw-r--r--lldb/test/API/functionalities/thread/state/TestThreadStates.py1
-rw-r--r--lldb/test/API/lang/c/step-target/TestStepTarget.py24
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py7
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp10
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h7
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp1
-rw-r--r--lldb/test/API/lang/cpp/lambdas/TestLambdas.py10
-rw-r--r--lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py3
-rw-r--r--lldb/test/API/lang/objc/failing-description/Makefile3
-rw-r--r--lldb/test/API/lang/objc/failing-description/TestObjCFailingDescription.py17
-rw-r--r--lldb/test/API/lang/objc/failing-description/main.m21
-rw-r--r--lldb/test/API/lang/objc/struct-description/Makefile2
-rw-r--r--lldb/test/API/lang/objc/struct-description/TestObjCStructDescription.py18
-rw-r--r--lldb/test/API/lang/objc/struct-description/main.m12
-rw-r--r--lldb/test/API/lit.cfg.py1
-rw-r--r--lldb/test/API/python_api/basename/Makefile3
-rw-r--r--lldb/test/API/python_api/basename/TestGetBaseName.py37
-rw-r--r--lldb/test/API/python_api/basename/main.cpp16
-rw-r--r--lldb/test/API/python_api/find_in_memory/address_ranges_helper.py33
-rw-r--r--lldb/test/API/python_api/run_locker/TestRunLocker.py4
-rw-r--r--lldb/test/API/python_api/sbmodule/Makefile11
-rw-r--r--lldb/test/API/python_api/sbmodule/TestSBModule.py39
-rw-r--r--lldb/test/API/python_api/sbmodule/a.c11
-rw-r--r--lldb/test/API/python_api/sbmodule/b.c11
-rw-r--r--lldb/test/API/python_api/sbmodule/main.c2
-rw-r--r--lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py47
-rw-r--r--lldb/test/API/python_api/value/TestValueAPI.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py10
-rw-r--r--lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py81
-rw-r--r--lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py4
-rw-r--r--lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py33
-rw-r--r--lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py22
-rw-r--r--lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py15
-rw-r--r--lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/console/TestDAP_console.py13
-rw-r--r--lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/io/TestDAP_io.py27
-rw-r--r--lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py27
-rw-r--r--lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py10
-rw-r--r--lldb/test/API/tools/lldb-dap/module/TestDAP_module.py10
-rw-r--r--lldb/test/API/tools/lldb-dap/moduleSymbols/Makefile3
-rw-r--r--lldb/test/API/tools/lldb-dap/moduleSymbols/TestDAP_moduleSymbols.py40
-rw-r--r--lldb/test/API/tools/lldb-dap/moduleSymbols/main.c9
-rw-r--r--lldb/test/API/tools/lldb-dap/output/TestDAP_output.py6
-rwxr-xr-xlldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/startDebugging/TestDAP_startDebugging.py1
-rw-r--r--lldb/test/Shell/Expr/TestLambdaExprImport.test26
-rw-r--r--lldb/test/Shell/Recognizer/ubsan_add_overflow.test4
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp8
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp87
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/unknown-udt-decl.ll56
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/ast-restore.test6
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test5
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/calling-conventions-x86.test12
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/class-layout.test12
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/enums-layout.test6
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/vbases.test1
-rw-r--r--lldb/test/Shell/Symtab/Inputs/simple.wasm.yaml254
-rw-r--r--lldb/test/Shell/Symtab/symtab-wasm.test16
-rw-r--r--lldb/tools/CMakeLists.txt1
-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/debugserver.cpp33
-rw-r--r--lldb/tools/driver/CMakeLists.txt2
-rw-r--r--lldb/tools/driver/Options.td417
-rw-r--r--lldb/tools/lldb-dap/Breakpoint.cpp31
-rw-r--r--lldb/tools/lldb-dap/CMakeLists.txt4
-rw-r--r--lldb/tools/lldb-dap/DAP.cpp304
-rw-r--r--lldb/tools/lldb-dap/DAP.h37
-rw-r--r--lldb/tools/lldb-dap/EventHelper.cpp30
-rw-r--r--lldb/tools/lldb-dap/EventHelper.h4
-rw-r--r--lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp173
-rw-r--r--lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp6
-rw-r--r--lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp2
-rw-r--r--lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp90
-rw-r--r--lldb/tools/lldb-dap/Handler/RequestHandler.h23
-rw-r--r--lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp1
-rw-r--r--lldb/tools/lldb-dap/Protocol/DAPTypes.cpp69
-rw-r--r--lldb/tools/lldb-dap/Protocol/DAPTypes.h85
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp13
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolBase.h4
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp28
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolRequests.h61
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp126
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolTypes.h91
-rw-r--r--lldb/tools/lldb-dap/SourceBreakpoint.cpp101
-rw-r--r--lldb/tools/lldb-dap/SourceBreakpoint.h7
-rw-r--r--lldb/tools/lldb-dap/Transport.cpp5
-rw-r--r--lldb/tools/lldb-dap/Transport.h11
-rw-r--r--lldb/tools/lldb-dap/package-lock.json513
-rw-r--r--lldb/tools/lldb-dap/package.json55
-rw-r--r--lldb/tools/lldb-dap/src-ts/debug-adapter-factory.ts99
-rw-r--r--lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts28
-rw-r--r--lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts22
-rw-r--r--lldb/tools/lldb-dap/src-ts/extension.ts10
-rw-r--r--lldb/tools/lldb-dap/src-ts/index.d.ts14
-rw-r--r--lldb/tools/lldb-dap/src-ts/lldb-dap-server.ts54
-rw-r--r--lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts1
-rw-r--r--lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts127
-rw-r--r--lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts67
-rw-r--r--lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts115
-rw-r--r--lldb/tools/lldb-dap/src-ts/webview/tsconfig.json15
-rw-r--r--lldb/tools/lldb-dap/tool/lldb-dap.cpp24
-rw-r--r--lldb/tools/lldb-dap/tsconfig.json2
-rw-r--r--lldb/tools/lldb-mcp/CMakeLists.txt33
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp-Info.plist.in21
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp.cpp84
-rw-r--r--lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp5
-rw-r--r--lldb/unittests/CMakeLists.txt4
-rw-r--r--lldb/unittests/Core/CMakeLists.txt1
-rw-r--r--lldb/unittests/Core/MangledTest.cpp8
-rw-r--r--lldb/unittests/Core/Value.cpp39
-rw-r--r--lldb/unittests/DAP/CMakeLists.txt1
-rw-r--r--lldb/unittests/DAP/DAPTest.cpp17
-rw-r--r--lldb/unittests/DAP/DAPTypesTest.cpp60
-rw-r--r--lldb/unittests/DAP/Handler/DisconnectTest.cpp24
-rw-r--r--lldb/unittests/DAP/ProtocolTypesTest.cpp79
-rw-r--r--lldb/unittests/DAP/TestBase.cpp87
-rw-r--r--lldb/unittests/DAP/TestBase.h104
-rw-r--r--lldb/unittests/Editline/EditlineTest.cpp3
-rw-r--r--lldb/unittests/Expression/DWARFExpressionTest.cpp4
-rw-r--r--lldb/unittests/Host/JSONTransportTest.cpp477
-rw-r--r--lldb/unittests/Host/MainLoopTest.cpp3
-rw-r--r--lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp120
-rw-r--r--lldb/unittests/Language/CPlusPlus/CMakeLists.txt1
-rw-r--r--lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp205
-rw-r--r--lldb/unittests/ObjectFile/ELF/TestObjectFileELF.cpp81
-rw-r--r--lldb/unittests/Protocol/CMakeLists.txt1
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPServerTest.cpp304
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPTest.cpp118
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPTestUtilities.h40
-rw-r--r--lldb/unittests/ProtocolServer/CMakeLists.txt11
-rw-r--r--lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp325
-rw-r--r--lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h26
-rw-r--r--lldb/unittests/TestingSupport/TestUtilities.h14
-rw-r--r--lldb/unittests/Utility/ScalarTest.cpp18
-rw-r--r--lldb/unittests/Utility/XcodeSDKTest.cpp11
-rw-r--r--lldb/unittests/ValueObject/DILLexerTests.cpp42
-rw-r--r--lldb/utils/TableGen/LLDBOptionDefEmitter.cpp29
-rw-r--r--lldb/utils/lui/lldbutil.py7
383 files changed, 15212 insertions, 5389 deletions
diff --git a/lldb/cmake/caches/Apple-lldb-Xcode.cmake b/lldb/cmake/caches/Apple-lldb-Xcode.cmake
index 98d68b1..2372239 100644
--- a/lldb/cmake/caches/Apple-lldb-Xcode.cmake
+++ b/lldb/cmake/caches/Apple-lldb-Xcode.cmake
@@ -1,7 +1,7 @@
include(${CMAKE_CURRENT_LIST_DIR}/Apple-lldb-base.cmake)
set(CMAKE_GENERATOR Xcode CACHE STRING "")
-set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "")
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13 CACHE STRING "")
set(CMAKE_XCODE_GENERATE_SCHEME ON CACHE BOOL "")
set(LLDB_BUILD_FRAMEWORK ON CACHE BOOL "")
diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake
index a9679d6..c65c7e2 100644
--- a/lldb/cmake/modules/LLDBConfig.cmake
+++ b/lldb/cmake/modules/LLDBConfig.cmake
@@ -68,7 +68,6 @@ add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in
option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)
option(LLDB_BUILD_FRAMEWORK "Build LLDB.framework (Darwin only)" OFF)
option(LLDB_ENABLE_PROTOCOL_SERVERS "Enable protocol servers (e.g. MCP) in LLDB" ON)
-option(LLDB_ENABLE_PYTHON_LIMITED_API "Force LLDB to only use the Python Limited API (requires SWIG 4.2 or later)" OFF)
option(LLDB_NO_INSTALL_DEFAULT_RPATH "Disable default RPATH settings in binaries" OFF)
option(LLDB_USE_SYSTEM_DEBUGSERVER "Use the system's debugserver for testing (Darwin only)." OFF)
option(LLDB_SKIP_STRIP "Whether to skip stripping of binaries when installing lldb." OFF)
@@ -174,11 +173,20 @@ if (LLDB_ENABLE_PYTHON)
${default_embed_python_home})
include_directories(${Python3_INCLUDE_DIRS})
+
if (LLDB_EMBED_PYTHON_HOME)
get_filename_component(PYTHON_HOME "${Python3_EXECUTABLE}" DIRECTORY)
set(LLDB_PYTHON_HOME "${PYTHON_HOME}" CACHE STRING
"Path to use as PYTHONHOME in lldb. If a relative path is specified, it will be resolved at runtime relative to liblldb directory.")
endif()
+
+ if (SWIG_VERSION VERSION_GREATER_EQUAL "4.2" AND NOT LLDB_EMBED_PYTHON_HOME)
+ set(default_enable_python_limited_api ON)
+ else()
+ set(default_enable_python_limited_api OFF)
+ endif()
+ option(LLDB_ENABLE_PYTHON_LIMITED_API "Force LLDB to only use the Python Limited API (requires SWIG 4.2 or later)"
+ ${default_enable_python_limited_api})
endif()
if (LLVM_EXTERNAL_CLANG_SOURCE_DIR)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 783432d..67328939 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/resources/lldbdap.md b/lldb/docs/resources/lldbdap.md
index 955713d..3838c82 100644
--- a/lldb/docs/resources/lldbdap.md
+++ b/lldb/docs/resources/lldbdap.md
@@ -170,3 +170,26 @@ This is also very simple, just run:
```bash
npm run format
```
+
+## Working with the VS Code extension from another extension
+
+The VS Code extension exposes the following [VS Code
+commands](https://code.visualstudio.com/api/extension-guides/command),
+which can be invoked by other debugger extensions to leverage this extension's
+settings and logic. The commands help resolve configuration, create adapter
+descriptor, and get the lldb-dap process for state tracking, additional
+interaction, and telemetry.
+
+```
+// Resolve debug configuration
+const resolvedConfiguration = await vscode.commands.executeCommand("lldb-dap.resolveDebugConfiguration", folder, configuration, token);
+
+// Resolve debug configuration with substituted variables
+const resolvedConfigurationWithSubstitutedVariables = await vscode.commands.executeCommand("lldb-dap.resolveDebugConfigurationWithSubstitutedVariables", folder, configuration, token);
+
+// Create debug adapter descriptor
+const adapterDescriptor = await vscode.commands.executeCommand("lldb-dap.createDebugAdapterDescriptor", session, executable);
+
+// Get DAP server process
+const process = await vscode.commands.executeCommand("lldb-dap.getServerProcess");
+```
diff --git a/lldb/docs/use/map.rst b/lldb/docs/use/map.rst
index ea166bf..da566e7 100644
--- a/lldb/docs/use/map.rst
+++ b/lldb/docs/use/map.rst
@@ -1126,6 +1126,20 @@ Save binary memory data starting at ``0x1000`` and ending at ``0x2000`` to a fil
(lldb) memory read --outfile /tmp/mem.bin --binary 0x1000 0x2000
(lldb) me r -o /tmp/mem.bin -b 0x1000 0x2000
+
+Print information about memory regions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: shell
+
+ (gdb) info proc mappings
+
+.. code-block:: shell
+
+ (lldb) memory region --all
+ (lldb) me reg --all
+
+
Get information about a specific heap allocation (macOS only)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/lldb/docs/use/remote.rst b/lldb/docs/use/remote.rst
index e2009cc..05b59a9 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/crashlog.py b/lldb/examples/python/crashlog.py
index bb20f3a..b466be6 100755
--- a/lldb/examples/python/crashlog.py
+++ b/lldb/examples/python/crashlog.py
@@ -1540,13 +1540,19 @@ def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result):
}
)
)
+
+ crashlog_sd = lldb.SBStructuredData()
+ crashlog_sd.SetGenericValue(
+ lldb.SBScriptObject(crashlog, lldb.eScriptLanguagePython)
+ )
+ structured_data.SetValueForKey("crashlog", crashlog_sd)
+
launch_info = lldb.SBLaunchInfo(None)
launch_info.SetProcessPluginName("ScriptedProcess")
launch_info.SetScriptedProcessClassName(
"crashlog_scripted_process.CrashLogScriptedProcess"
)
launch_info.SetScriptedProcessDictionary(structured_data)
- launch_info.SetLaunchFlags(lldb.eLaunchFlagStopAtEntry)
error = lldb.SBError()
process = target.Launch(launch_info, error)
@@ -1554,9 +1560,6 @@ def load_crashlog_in_scripted_process(debugger, crashlog_path, options, result):
if not process or error.Fail():
raise InteractiveCrashLogException("couldn't launch Scripted Process", error)
- process.GetScriptedImplementation().set_crashlog(crashlog)
- process.Continue()
-
if not options.skip_status:
@contextlib.contextmanager
diff --git a/lldb/examples/python/crashlog_scripted_process.py b/lldb/examples/python/crashlog_scripted_process.py
index f54a8df..6c6eec8 100644
--- a/lldb/examples/python/crashlog_scripted_process.py
+++ b/lldb/examples/python/crashlog_scripted_process.py
@@ -10,8 +10,7 @@ from lldb.macosx.crashlog import CrashLog, CrashLogParser
class CrashLogScriptedProcess(ScriptedProcess):
- def set_crashlog(self, crashlog):
- self.crashlog = crashlog
+ def parse_crashlog(self):
if self.crashlog.process_id:
if type(self.crashlog.process_id) is int:
self.pid = self.crashlog.process_id
@@ -29,8 +28,6 @@ class CrashLogScriptedProcess(ScriptedProcess):
if hasattr(self.crashlog, "asb"):
self.extended_thread_info = self.crashlog.asb
- crashlog.load_images(self.options, self.loaded_images)
-
for thread in self.crashlog.threads:
if (
hasattr(thread, "app_specific_backtrace")
@@ -92,10 +89,21 @@ class CrashLogScriptedProcess(ScriptedProcess):
no_parallel_image_loading.GetBooleanValue()
)
+ self.crashlog = None
+ crashlog = args.GetValueForKey("crashlog")
+ if crashlog and crashlog.IsValid():
+ if crashlog.GetType() == lldb.eStructuredDataTypeGeneric:
+ self.crashlog = crashlog.GetGenericValue()
+
+ if not self.crashlog:
+ # Return error
+ return
+
self.pid = super().get_process_id()
self.crashed_thread_idx = 0
self.exception = None
self.extended_thread_info = None
+ self.parse_crashlog()
def read_memory_at_address(
self, addr: int, size: int, error: lldb.SBError
@@ -104,8 +112,8 @@ class CrashLogScriptedProcess(ScriptedProcess):
return lldb.SBData()
def get_loaded_images(self):
- # TODO: Iterate over corefile_target modules and build a data structure
- # from it.
+ if len(self.loaded_images) == 0:
+ self.crashlog.load_images(self.options, self.loaded_images)
return self.loaded_images
def should_stop(self) -> bool:
diff --git a/lldb/include/lldb/API/SBError.h b/lldb/include/lldb/API/SBError.h
index 58b45eb..6a4a39c 100644
--- a/lldb/include/lldb/API/SBError.h
+++ b/lldb/include/lldb/API/SBError.h
@@ -87,6 +87,7 @@ protected:
friend class SBDebugger;
friend class SBFile;
friend class SBFormat;
+ friend class SBFrame;
friend class SBHostOS;
friend class SBPlatform;
friend class SBProcess;
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index 3635ee5..08de060 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -233,6 +233,10 @@ protected:
void SetFrameSP(const lldb::StackFrameSP &lldb_object_sp);
+ /// Return an SBValue containing an error message that warns the process is
+ /// not currently stopped.
+ static SBValue CreateProcessIsRunningExprEvalError();
+
lldb::ExecutionContextRefSP m_opaque_sp;
};
diff --git a/lldb/include/lldb/API/SBFunction.h b/lldb/include/lldb/API/SBFunction.h
index 0a8aeef..e703ae5 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/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 8533206..4009ca1 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -296,6 +296,11 @@ public:
/// Remove any global modules which are no longer needed.
static void GarbageCollectAllocatedModules();
+ /// If this Module represents a specific object or part within a larger file,
+ /// returns the name of that object or part. Otherwise, returns
+ /// nullptr.
+ const char *GetObjectName() const;
+
private:
friend class SBAddress;
friend class SBFrame;
diff --git a/lldb/include/lldb/API/SBStructuredData.h b/lldb/include/lldb/API/SBStructuredData.h
index f96e169..dfd8ec0 100644
--- a/lldb/include/lldb/API/SBStructuredData.h
+++ b/lldb/include/lldb/API/SBStructuredData.h
@@ -109,6 +109,35 @@ public:
/// Return the generic pointer if this data structure is a generic type.
lldb::SBScriptObject GetGenericValue() const;
+ /// Set the value corresponding to a key. If this data structure
+ /// is not a dictionary type, reset the type to be dictionary and overwrite
+ /// the previous data.
+ void SetValueForKey(const char *key, SBStructuredData &value);
+
+ /// Change the type to unsigned interger and overwrite the previous data with
+ /// the new value.
+ void SetUnsignedIntegerValue(uint64_t value);
+
+ /// Change the type to signed interger and overwrite the previous data with
+ /// the new value.
+ void SetSignedIntegerValue(int64_t value);
+
+ /// Change the type to float and overwrite the previous data with the new
+ /// value.
+ void SetFloatValue(double value);
+
+ /// Change the type to boolean and overwrite the previous data with the new
+ /// value.
+ void SetBooleanValue(bool value);
+
+ /// Change the type to string and overwrite the previous data with the new
+ /// value.
+ void SetStringValue(const char *value);
+
+ /// Change the type to generic and overwrite the previous data with the new
+ /// value.
+ void SetGenericValue(SBScriptObject value);
+
protected:
friend class SBAttachInfo;
friend class SBCommandReturnObject;
diff --git a/lldb/include/lldb/API/SBSymbol.h b/lldb/include/lldb/API/SBSymbol.h
index 9452188..580458e 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,
@@ -85,6 +87,12 @@ public:
SymbolType GetType();
+ /// Get the ID of this symbol, usually the original symbol table index.
+ ///
+ /// \returns
+ /// Returns the ID of this symbol.
+ uint32_t GetID();
+
bool operator==(const lldb::SBSymbol &rhs) const;
bool operator!=(const lldb::SBSymbol &rhs) const;
@@ -99,6 +107,15 @@ public:
// other than the actual symbol table itself in the object file.
bool IsSynthetic();
+ /// Returns true if the symbol is a debug symbol.
+ bool IsDebug();
+
+ /// Get the string representation of a symbol type.
+ static const char *GetTypeAsString(lldb::SymbolType symbol_type);
+
+ /// Get the symbol type from a string representation.
+ static lldb::SymbolType GetTypeFromString(const char *str);
+
protected:
lldb_private::Symbol *get();
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 2776a8f..62cdd34 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -324,6 +324,16 @@ public:
lldb::SBModule FindModule(const lldb::SBFileSpec &file_spec);
+ /// Find a module with the given module specification.
+ ///
+ /// \param[in] module_spec
+ /// A lldb::SBModuleSpec object that contains module specification.
+ ///
+ /// \return
+ /// A lldb::SBModule object that represents the found module, or an
+ /// invalid SBModule object if no module was found.
+ lldb::SBModule FindModule(const lldb::SBModuleSpec &module_spec);
+
/// Find compile units related to *this target and passed source
/// file.
///
@@ -658,6 +668,14 @@ public:
lldb::LanguageType symbol_language,
const SBFileSpecList &module_list, const SBFileSpecList &comp_unit_list);
+ lldb::SBBreakpoint BreakpointCreateByName(
+ const char *symbol_name,
+ uint32_t
+ name_type_mask, // Logical OR one or more FunctionNameType enum bits
+ lldb::LanguageType symbol_language, lldb::addr_t offset,
+ bool offset_is_insn_count, const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list);
+
#ifdef SWIG
lldb::SBBreakpoint BreakpointCreateByNames(
const char **symbol_name, uint32_t num_names,
diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolver.h b/lldb/include/lldb/Breakpoint/BreakpointResolver.h
index 52cd70e..243acee 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointResolver.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointResolver.h
@@ -45,9 +45,9 @@ public:
/// The breakpoint that owns this resolver.
/// \param[in] resolverType
/// The concrete breakpoint resolver type for this breakpoint.
- BreakpointResolver(const lldb::BreakpointSP &bkpt,
- unsigned char resolverType,
- lldb::addr_t offset = 0);
+ BreakpointResolver(const lldb::BreakpointSP &bkpt, unsigned char resolverType,
+ lldb::addr_t offset = 0,
+ bool offset_is_insn_count = false);
/// The Destructor is virtual, all significant breakpoint resolvers derive
/// from this class.
@@ -76,6 +76,7 @@ public:
void SetOffset(lldb::addr_t offset);
lldb::addr_t GetOffset() const { return m_offset; }
+ lldb::addr_t GetOffsetIsInsnCount() const { return m_offset_is_insn_count; }
/// In response to this method the resolver scans all the modules in the
/// breakpoint's target, and adds any new locations it finds.
@@ -220,6 +221,8 @@ private:
lldb::BreakpointWP m_breakpoint; // This is the breakpoint we add locations to.
lldb::addr_t m_offset; // A random offset the user asked us to add to any
// breakpoints we set.
+ bool m_offset_is_insn_count; // Use the offset as an instruction count
+ // instead of an address offset.
// Subclass identifier (for llvm isa/dyn_cast)
const unsigned char SubclassID;
diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverName.h b/lldb/include/lldb/Breakpoint/BreakpointResolverName.h
index c83814c..48b3eda 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointResolverName.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointResolverName.h
@@ -27,7 +27,7 @@ public:
lldb::FunctionNameType name_type_mask,
lldb::LanguageType language,
Breakpoint::MatchType type, lldb::addr_t offset,
- bool skip_prologue);
+ bool offset_is_insn_count, bool skip_prologue);
// This one takes an array of names. It is always MatchType = Exact.
BreakpointResolverName(const lldb::BreakpointSP &bkpt, const char *names[],
diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h
index 21bacb1..db186dd 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:
@@ -291,6 +291,8 @@ public:
size_t GetSize() const;
+ size_t GetTotalByteSize() const;
+
uint32_t GetMaxOpcocdeByteSize() const;
lldb::InstructionSP GetInstructionAtIndex(size_t idx) const;
@@ -314,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,
@@ -397,6 +398,7 @@ public:
eOptionMarkPCAddress =
(1u << 3), // Mark the disassembly line the contains the PC
eOptionShowControlFlowKind = (1u << 4),
+ eOptionVariableAnnotations = (1u << 5),
};
enum HexImmediateStyle {
@@ -564,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/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index eb9a58c..47f1c6a8 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/ProtocolServer.h b/lldb/include/lldb/Core/ProtocolServer.h
index 937256c..fcb91ea 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/Core/StructuredDataImpl.h b/lldb/include/lldb/Core/StructuredDataImpl.h
index fd0a7b9..b88962b 100644
--- a/lldb/include/lldb/Core/StructuredDataImpl.h
+++ b/lldb/include/lldb/Core/StructuredDataImpl.h
@@ -81,6 +81,41 @@ public:
void SetObjectSP(const StructuredData::ObjectSP &obj) { m_data_sp = obj; }
+ void SetValueForKey(llvm::StringRef key,
+ const StructuredData::ObjectSP &value) {
+ if (!m_data_sp ||
+ m_data_sp->GetType() != lldb::eStructuredDataTypeDictionary) {
+ m_data_sp = StructuredData::FromKeyValue(key, value);
+ } else if (StructuredData::Dictionary *dict =
+ m_data_sp->GetAsDictionary()) {
+ dict->AddItem(key, value);
+ }
+ }
+
+ void SetUnsignedIntegerValue(uint64_t value) {
+ m_data_sp = StructuredData::FromInteger(value);
+ }
+
+ void SetSignedIntegerValue(int64_t value) {
+ m_data_sp = StructuredData::FromInteger(value);
+ }
+
+ void SetFloatValue(double value) {
+ m_data_sp = StructuredData::FromFloat(value);
+ }
+
+ void SetBooleanValue(bool value) {
+ m_data_sp = StructuredData::FromBoolean(value);
+ }
+
+ void SetStringValue(std::string value) {
+ m_data_sp = StructuredData::FromString(std::move(value));
+ }
+
+ void SetGenericValue(void *value) {
+ m_data_sp = StructuredData::FromGeneric(value);
+ }
+
lldb::StructuredDataType GetType() const {
return (m_data_sp ? m_data_sp->GetType() :
lldb::eStructuredDataTypeInvalid);
diff --git a/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h b/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h
index cdb620e..70166f3 100644
--- a/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h
+++ b/lldb/include/lldb/DataFormatters/DumpValueObjectOptions.h
@@ -76,7 +76,9 @@ public:
DumpValueObjectOptions &SetShowLocation(bool show = false);
- DumpValueObjectOptions &SetUseObjectiveC(bool use = false);
+ DumpValueObjectOptions &DisableObjectDescription();
+
+ DumpValueObjectOptions &SetUseObjectDescription(bool use = false);
DumpValueObjectOptions &SetShowSummary(bool show = true);
@@ -143,13 +145,14 @@ public:
ChildPrintingDecider m_child_printing_decider;
PointerAsArraySettings m_pointer_as_array;
unsigned m_expand_ptr_type_flags = 0;
+ // The following flags commonly default to false.
bool m_use_synthetic : 1;
bool m_scope_already_checked : 1;
bool m_flat_output : 1;
bool m_ignore_cap : 1;
bool m_show_types : 1;
bool m_show_location : 1;
- bool m_use_objc : 1;
+ bool m_use_object_desc : 1;
bool m_hide_root_type : 1;
bool m_hide_root_name : 1;
bool m_hide_name : 1;
diff --git a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
index f9deb6e..fdea368 100644
--- a/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
+++ b/lldb/include/lldb/DataFormatters/ValueObjectPrinter.h
@@ -75,8 +75,6 @@ protected:
void SetupMostSpecializedValue();
- llvm::Expected<std::string> GetDescriptionForDisplay();
-
const char *GetRootNameForDisplay();
bool ShouldPrintValueObject();
@@ -108,8 +106,7 @@ protected:
bool PrintValueAndSummaryIfNeeded(bool &value_printed, bool &summary_printed);
- llvm::Error PrintObjectDescriptionIfNeeded(bool value_printed,
- bool summary_printed);
+ void PrintObjectDescriptionIfNeeded(std::optional<std::string> object_desc);
bool
ShouldPrintChildren(DumpValueObjectOptions::PointerDepth &curr_ptr_depth);
@@ -141,6 +138,7 @@ protected:
private:
bool ShouldShowName() const;
+ bool ShouldPrintObjectDescription();
ValueObject &m_orig_valobj;
/// Cache the current "most specialized" value. Don't use this
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index 8fcc5d3..1f8464c 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/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h
index b6a95ff..a6aaacd 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 4087cdf..0be60a8 100644
--- a/lldb/include/lldb/Host/JSONTransport.h
+++ b/lldb/include/lldb/Host/JSONTransport.h
@@ -13,125 +13,284 @@
#ifndef LLDB_HOST_JSONTRANSPORT_H
#define LLDB_HOST_JSONTRANSPORT_H
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Utility/IOObject.h"
+#include "lldb/Utility/Status.h"
#include "lldb/lldb-forward.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
-#include <chrono>
+#include "llvm/Support/raw_ostream.h"
+#include <string>
#include <system_error>
+#include <variant>
+#include <vector>
namespace lldb_private {
-class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
+class TransportUnhandledContentsError
+ : public llvm::ErrorInfo<TransportUnhandledContentsError> {
public:
static char ID;
- TransportEOFError() = default;
+ explicit TransportUnhandledContentsError(std::string unhandled_contents);
- void log(llvm::raw_ostream &OS) const override {
- OS << "transport end of file reached";
- }
- std::error_code convertToErrorCode() const override {
- return llvm::inconvertibleErrorCode();
+ void log(llvm::raw_ostream &OS) const override;
+ std::error_code convertToErrorCode() const override;
+
+ const std::string &getUnhandledContents() const {
+ return m_unhandled_contents;
}
+
+private:
+ std::string m_unhandled_contents;
};
-class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
+/// A transport is responsible for maintaining the connection to a client
+/// application, and reading/writing structured messages to it.
+///
+/// Transports have limited thread safety requirements:
+/// - Messages will not be sent concurrently.
+/// - Messages MAY be sent while Run() is reading, or its callback is active.
+template <typename Req, typename Resp, typename Evt> class Transport {
public:
- static char ID;
+ using Message = std::variant<Req, Resp, Evt>;
- TransportTimeoutError() = default;
+ virtual ~Transport() = default;
- void log(llvm::raw_ostream &OS) const override {
- OS << "transport operation timed out";
- }
- std::error_code convertToErrorCode() const override {
- return std::make_error_code(std::errc::timed_out);
- }
-};
+ /// Sends an event, a message that does not require a response.
+ virtual llvm::Error Send(const Evt &) = 0;
+ /// Sends a request, a message that expects a response.
+ virtual llvm::Error Send(const Req &) = 0;
+ /// Sends a response to a specific request.
+ virtual llvm::Error Send(const Resp &) = 0;
-class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
-public:
- static char ID;
+ /// Implemented to handle incoming messages. (See Run() below).
+ class MessageHandler {
+ public:
+ virtual ~MessageHandler() = default;
+ /// Called when an event is received.
+ virtual void Received(const Evt &) = 0;
+ /// Called when a request is received.
+ virtual void Received(const Req &) = 0;
+ /// Called when a response is received.
+ virtual void Received(const Resp &) = 0;
- TransportInvalidError() = default;
+ /// Called when an error occurs while reading from the transport.
+ ///
+ /// NOTE: This does *NOT* indicate that a specific request failed, but that
+ /// there was an error in the underlying transport.
+ virtual void OnError(llvm::Error) = 0;
- void log(llvm::raw_ostream &OS) const override {
- OS << "transport IO object invalid";
- }
- std::error_code convertToErrorCode() const override {
- return std::make_error_code(std::errc::not_connected);
+ /// Called on EOF or client disconnect.
+ virtual void OnClosed() = 0;
+ };
+
+ using MessageHandlerSP = std::shared_ptr<MessageHandler>;
+
+ /// RegisterMessageHandler registers the Transport with the given MainLoop and
+ /// handles any incoming messages using the given MessageHandler.
+ ///
+ /// If an unexpected error occurs, the MainLoop will be terminated and a log
+ /// message will include additional information about the termination reason.
+ virtual llvm::Expected<MainLoop::ReadHandleUP>
+ RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) = 0;
+
+protected:
+ template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
+ Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
}
+ virtual void Log(llvm::StringRef message) = 0;
};
-/// A transport class that uses JSON for communication.
-class JSONTransport {
+/// A JSONTransport will encode and decode messages using JSON.
+template <typename Req, typename Resp, typename Evt>
+class JSONTransport : public Transport<Req, Resp, Evt> {
public:
- JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
- virtual ~JSONTransport() = default;
-
- /// Transport is not copyable.
- /// @{
- JSONTransport(const JSONTransport &rhs) = delete;
- void operator=(const JSONTransport &rhs) = delete;
- /// @}
-
- /// Writes a message to the output stream.
- template <typename T> llvm::Error Write(const T &t) {
- const std::string message = llvm::formatv("{0}", toJSON(t)).str();
- return WriteImpl(message);
- }
+ using Transport<Req, Resp, Evt>::Transport;
+ using MessageHandler = typename Transport<Req, Resp, Evt>::MessageHandler;
- /// Reads the next message from the input stream.
- template <typename T>
- llvm::Expected<T> Read(const std::chrono::microseconds &timeout) {
- llvm::Expected<std::string> message = ReadImpl(timeout);
- if (!message)
- return message.takeError();
- return llvm::json::parse<T>(/*JSON=*/*message);
+ JSONTransport(lldb::IOObjectSP in, lldb::IOObjectSP out)
+ : m_in(in), m_out(out) {}
+
+ llvm::Error Send(const Evt &evt) override { return Write(evt); }
+ llvm::Error Send(const Req &req) override { return Write(req); }
+ llvm::Error Send(const Resp &resp) override { return Write(resp); }
+
+ llvm::Expected<MainLoop::ReadHandleUP>
+ RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) override {
+ Status status;
+ MainLoop::ReadHandleUP read_handle = loop.RegisterReadObject(
+ m_in,
+ std::bind(&JSONTransport::OnRead, this, std::placeholders::_1,
+ std::ref(handler)),
+ status);
+ if (status.Fail()) {
+ return status.takeError();
+ }
+ return read_handle;
}
+ /// Public for testing purposes, otherwise this should be an implementation
+ /// detail.
+ static constexpr size_t kReadBufferSize = 1024;
+
protected:
- virtual void Log(llvm::StringRef message);
+ virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
+ virtual std::string Encode(const llvm::json::Value &message) = 0;
+ llvm::Error Write(const llvm::json::Value &message) {
+ this->Logv("<-- {0}", message);
+ std::string output = Encode(message);
+ size_t bytes_written = output.size();
+ return m_out->Write(output.data(), bytes_written).takeError();
+ }
+
+ llvm::SmallString<kReadBufferSize> m_buffer;
- virtual llvm::Error WriteImpl(const std::string &message) = 0;
- virtual llvm::Expected<std::string>
- ReadImpl(const std::chrono::microseconds &timeout) = 0;
+private:
+ void OnRead(MainLoopBase &loop, MessageHandler &handler) {
+ char buf[kReadBufferSize];
+ size_t num_bytes = sizeof(buf);
+ if (Status status = m_in->Read(buf, num_bytes); status.Fail()) {
+ handler.OnError(status.takeError());
+ return;
+ }
- lldb::IOObjectSP m_input;
- lldb::IOObjectSP m_output;
+ if (num_bytes)
+ m_buffer.append(llvm::StringRef(buf, num_bytes));
+
+ // If the buffer has contents, try parsing any pending messages.
+ if (!m_buffer.empty()) {
+ llvm::Expected<std::vector<std::string>> raw_messages = Parse();
+ if (llvm::Error error = raw_messages.takeError()) {
+ handler.OnError(std::move(error));
+ return;
+ }
+
+ for (const std::string &raw_message : *raw_messages) {
+ llvm::Expected<typename Transport<Req, Resp, Evt>::Message> message =
+ llvm::json::parse<typename Transport<Req, Resp, Evt>::Message>(
+ raw_message);
+ if (!message) {
+ handler.OnError(message.takeError());
+ return;
+ }
+
+ std::visit([&handler](auto &&msg) { handler.Received(msg); }, *message);
+ }
+ }
+
+ // Check if we reached EOF.
+ if (num_bytes == 0) {
+ // EOF reached, but there may still be unhandled contents in the buffer.
+ if (!m_buffer.empty())
+ handler.OnError(llvm::make_error<TransportUnhandledContentsError>(
+ std::string(m_buffer.str())));
+ handler.OnClosed();
+ }
+ }
+
+ lldb::IOObjectSP m_in;
+ lldb::IOObjectSP m_out;
};
/// A transport class for JSON with a HTTP header.
-class HTTPDelimitedJSONTransport : public JSONTransport {
+template <typename Req, typename Resp, typename Evt>
+class HTTPDelimitedJSONTransport : public JSONTransport<Req, Resp, Evt> {
public:
- HTTPDelimitedJSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
- : JSONTransport(input, output) {}
- virtual ~HTTPDelimitedJSONTransport() = default;
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
protected:
- virtual llvm::Error WriteImpl(const std::string &message) override;
- virtual llvm::Expected<std::string>
- ReadImpl(const std::chrono::microseconds &timeout) override;
-
- // FIXME: Support any header.
- static constexpr llvm::StringLiteral kHeaderContentLength =
- "Content-Length: ";
- static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
+ /// Encodes messages based on
+ /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
+ std::string Encode(const llvm::json::Value &message) override {
+ std::string output;
+ std::string raw_message = llvm::formatv("{0}", message).str();
+ llvm::raw_string_ostream OS(output);
+ OS << kHeaderContentLength << kHeaderFieldSeparator << ' '
+ << std::to_string(raw_message.size()) << kEndOfHeader << raw_message;
+ return output;
+ }
+
+ /// Parses messages based on
+ /// https://microsoft.github.io/debug-adapter-protocol/overview#base-protocol
+ llvm::Expected<std::vector<std::string>> Parse() override {
+ std::vector<std::string> messages;
+ llvm::StringRef buffer = this->m_buffer;
+ while (buffer.contains(kEndOfHeader)) {
+ auto [headers, rest] = buffer.split(kEndOfHeader);
+ size_t content_length = 0;
+ // HTTP Headers are formatted like `<field-name> ':' [<field-value>]`.
+ for (const llvm::StringRef &header :
+ llvm::split(headers, kHeaderSeparator)) {
+ auto [key, value] = header.split(kHeaderFieldSeparator);
+ // 'Content-Length' is the only meaningful key at the moment. Others are
+ // ignored.
+ if (!key.equals_insensitive(kHeaderContentLength))
+ continue;
+
+ value = value.trim();
+ if (!llvm::to_integer(value, content_length, 10)) {
+ // Clear the buffer to avoid re-parsing this malformed message.
+ this->m_buffer.clear();
+ return llvm::createStringError(std::errc::invalid_argument,
+ "invalid content length: %s",
+ value.str().c_str());
+ }
+ }
+
+ // Check if we have enough data.
+ if (content_length > rest.size())
+ break;
+
+ llvm::StringRef body = rest.take_front(content_length);
+ buffer = rest.drop_front(content_length);
+ messages.emplace_back(body.str());
+ this->Logv("--> {0}", body);
+ }
+
+ // Store the remainder of the buffer for the next read callback.
+ this->m_buffer = buffer.str();
+
+ return std::move(messages);
+ }
+
+ static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
+ static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
+ static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
+ static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
};
/// A transport class for JSON RPC.
-class JSONRPCTransport : public JSONTransport {
+template <typename Req, typename Resp, typename Evt>
+class JSONRPCTransport : public JSONTransport<Req, Resp, Evt> {
public:
- JSONRPCTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
- : JSONTransport(input, output) {}
- virtual ~JSONRPCTransport() = default;
+ using JSONTransport<Req, Resp, Evt>::JSONTransport;
protected:
- virtual llvm::Error WriteImpl(const std::string &message) override;
- virtual llvm::Expected<std::string>
- ReadImpl(const std::chrono::microseconds &timeout) override;
+ std::string Encode(const llvm::json::Value &message) override {
+ return llvm::formatv("{0}{1}", message, kMessageSeparator).str();
+ }
+
+ llvm::Expected<std::vector<std::string>> Parse() override {
+ std::vector<std::string> messages;
+ llvm::StringRef buf = this->m_buffer;
+ while (buf.contains(kMessageSeparator)) {
+ auto [raw_json, rest] = buf.split(kMessageSeparator);
+ buf = rest;
+ messages.emplace_back(raw_json.str());
+ this->Logv("--> {0}", raw_json);
+ }
+
+ // Store the remainder of the buffer for the next read callback.
+ this->m_buffer = buf.str();
+
+ return messages;
+ }
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
};
diff --git a/lldb/include/lldb/Host/ProcessRunLock.h b/lldb/include/lldb/Host/ProcessRunLock.h
index 9468367..27d1094 100644
--- a/lldb/include/lldb/Host/ProcessRunLock.h
+++ b/lldb/include/lldb/Host/ProcessRunLock.h
@@ -41,9 +41,22 @@ public:
class ProcessRunLocker {
public:
ProcessRunLocker() = default;
+ ProcessRunLocker(ProcessRunLocker &&other) : m_lock(other.m_lock) {
+ other.m_lock = nullptr;
+ }
+ ProcessRunLocker &operator=(ProcessRunLocker &&other) {
+ if (this != &other) {
+ Unlock();
+ m_lock = other.m_lock;
+ other.m_lock = nullptr;
+ }
+ return *this;
+ }
~ProcessRunLocker() { Unlock(); }
+ bool IsLocked() const { return m_lock; }
+
// Try to lock the read lock, but only do so if there are no writers.
bool TryLock(ProcessRunLock *lock) {
if (m_lock) {
diff --git a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
index d048418..734a394 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/OptionGroupValueObjectDisplay.h b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h
index ebf26ce..4fd552a 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h
@@ -31,7 +31,7 @@ public:
bool AnyOptionWasSet() const {
return show_types || no_summary_depth != 0 || show_location ||
- flat_output || use_objc || max_depth != UINT32_MAX ||
+ flat_output || use_object_desc || max_depth != UINT32_MAX ||
ptr_depth != 0 || !use_synth || be_raw || ignore_cap ||
run_validator;
}
@@ -42,7 +42,7 @@ public:
lldb::Format format = lldb::eFormatDefault,
lldb::TypeSummaryImplSP summary_sp = lldb::TypeSummaryImplSP());
- bool show_types : 1, show_location : 1, flat_output : 1, use_objc : 1,
+ bool show_types : 1, show_location : 1, flat_output : 1, use_object_desc : 1,
use_synth : 1, be_raw : 1, ignore_cap : 1, run_validator : 1,
max_depth_is_default : 1;
diff --git a/lldb/include/lldb/Interpreter/OptionValue.h b/lldb/include/lldb/Interpreter/OptionValue.h
index f293a3a..9c99282 100644
--- a/lldb/include/lldb/Interpreter/OptionValue.h
+++ b/lldb/include/lldb/Interpreter/OptionValue.h
@@ -17,6 +17,7 @@
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/FileSpecList.h"
#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/UUID.h"
#include "lldb/lldb-defines.h"
@@ -61,6 +62,7 @@ public:
eDumpOptionDescription = (1u << 3),
eDumpOptionRaw = (1u << 4),
eDumpOptionCommand = (1u << 5),
+ eDumpOptionDefaultValue = (1u << 6),
eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue),
eDumpGroupHelp =
(eDumpOptionName | eDumpOptionType | eDumpOptionDescription),
@@ -338,6 +340,20 @@ protected:
// DeepCopy to it. Inherit from Cloneable to avoid doing this manually.
virtual lldb::OptionValueSP Clone() const = 0;
+ class DefaultValueFormat {
+ public:
+ DefaultValueFormat(Stream &stream) : stream(stream) {
+ stream.PutCString(" (default: ");
+ }
+ ~DefaultValueFormat() { stream.PutChar(')'); }
+
+ DefaultValueFormat(const DefaultValueFormat &) = delete;
+ DefaultValueFormat &operator=(const DefaultValueFormat &) = delete;
+
+ private:
+ Stream &stream;
+ };
+
lldb::OptionValueWP m_parent_wp;
std::function<void()> m_callback;
bool m_value_was_set = false; // This can be used to see if a value has been
diff --git a/lldb/include/lldb/Interpreter/OptionValueEnumeration.h b/lldb/include/lldb/Interpreter/OptionValueEnumeration.h
index a3a13ca..91ab454 100644
--- a/lldb/include/lldb/Interpreter/OptionValueEnumeration.h
+++ b/lldb/include/lldb/Interpreter/OptionValueEnumeration.h
@@ -72,6 +72,7 @@ public:
protected:
void SetEnumerations(const OptionEnumValues &enumerators);
+ void DumpEnum(Stream &strm, enum_type value);
enum_type m_current_value;
enum_type m_default_value;
diff --git a/lldb/include/lldb/Interpreter/Options.h b/lldb/include/lldb/Interpreter/Options.h
index 864bda6..2d06605 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/Protocol/MCP/MCPError.h b/lldb/include/lldb/Protocol/MCP/MCPError.h
index 2bdbb9b..55dd40f 100644
--- a/lldb/include/lldb/Protocol/MCP/MCPError.h
+++ b/lldb/include/lldb/Protocol/MCP/MCPError.h
@@ -26,7 +26,7 @@ public:
const std::string &getMessage() const { return m_message; }
- lldb_protocol::mcp::Error toProtcolError() const;
+ lldb_protocol::mcp::Error toProtocolError() const;
static constexpr int64_t kResourceNotFound = -32002;
static constexpr int64_t kInternalError = -32603;
diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h
index c43b068..6e1ffcb 100644
--- a/lldb/include/lldb/Protocol/MCP/Protocol.h
+++ b/lldb/include/lldb/Protocol/MCP/Protocol.h
@@ -18,90 +18,91 @@
#include <optional>
#include <string>
#include <variant>
+#include <vector>
namespace lldb_protocol::mcp {
-static llvm::StringLiteral kVersion = "2024-11-05";
+static llvm::StringLiteral kProtocolVersion = "2024-11-05";
+
+/// A Request or Response 'id'.
+///
+/// NOTE: This differs from the JSON-RPC 2.0 spec. The MCP spec says this must
+/// be a string or number, excluding a json 'null' as a valid id.
+using Id = std::variant<int64_t, std::string>;
/// A request that expects a response.
struct Request {
- uint64_t id = 0;
+ /// The request id.
+ Id id = 0;
+ /// The method to be invoked.
std::string method;
+ /// 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);
-
-struct ErrorInfo {
- int64_t code = 0;
- std::string message;
- std::string data;
+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,
};
-llvm::json::Value toJSON(const ErrorInfo &);
-bool fromJSON(const llvm::json::Value &, ErrorInfo &, llvm::json::Path);
-
struct Error {
- uint64_t id = 0;
- ErrorInfo error;
+ /// The error type that occurred.
+ int64_t code = 0;
+ /// A short description of the error. The message SHOULD be limited to a
+ /// concise single sentence.
+ std::string message;
+ /// 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::nullopt;
};
-
llvm::json::Value toJSON(const Error &);
bool fromJSON(const llvm::json::Value &, Error &, llvm::json::Path);
+bool operator==(const Error &, const Error &);
+/// A response to a request, either an error or a result.
struct Response {
- uint64_t id = 0;
- std::optional<llvm::json::Value> result;
- std::optional<ErrorInfo> error;
+ /// The request id.
+ Id id = 0;
+ /// The result of the request, either an Error or the JSON value of the
+ /// 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 &);
/// A notification which does not expect a response.
struct Notification {
+ /// The method to be invoked.
std::string method;
+ /// 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);
-
-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);
+bool operator==(const Notification &, const Notification &);
+
+/// A general message as defined by the JSON-RPC 2.0 spec.
+using Message = std::variant<Request, Response, Notification>;
+// With clang-cl and MSVC STL 202208, convertible can be false later if we do
+// 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 &);
/// A known resource that the server is capable of reading.
struct Resource {
@@ -112,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;
@@ -134,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;
@@ -172,16 +184,143 @@ 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 Message = std::variant<Request, Response, Notification, Error>;
+using ToolArguments = std::variant<std::monostate, llvm::json::Value>;
-bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
-llvm::json::Value toJSON(const Message &);
+/// 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;
-using ToolArguments = std::variant<std::monostate, llvm::json::Value>;
+ 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
diff --git a/lldb/include/lldb/Protocol/MCP/Resource.h b/lldb/include/lldb/Protocol/MCP/Resource.h
index 4835d34..158cffc 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
new file mode 100644
index 0000000..c6e78a9
--- /dev/null
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_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 "llvm/ADT/StringMap.h"
+#include "llvm/Support/Error.h"
+#include <mutex>
+
+namespace lldb_protocol::mcp {
+
+class MCPTransport
+ : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
+public:
+ using LogCallback = std::function<void(llvm::StringRef message)>;
+
+ MCPTransport(lldb::IOObjectSP in, lldb::IOObjectSP out,
+ std::string client_name, LogCallback log_callback = {})
+ : JSONRPCTransport(in, out), m_client_name(std::move(client_name)),
+ m_log_callback(log_callback) {}
+ virtual ~MCPTransport() = default;
+
+ void Log(llvm::StringRef message) override {
+ if (m_log_callback)
+ m_log_callback(llvm::formatv("{0}: {1}", m_client_name, message).str());
+ }
+
+private:
+ std::string m_client_name;
+ LogCallback m_log_callback;
+};
+
+/// 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;
+ lldb::pid_t pid;
+};
+llvm::json::Value toJSON(const ServerInfo &);
+bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path);
+
+class Server : public MCPTransport::MessageHandler {
+public:
+ 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:
+ ServerCapabilities GetCapabilities();
+
+ using RequestHandler =
+ std::function<llvm::Expected<Response>(const Request &)>;
+
+ void AddRequestHandlers();
+
+ void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
+
+ llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data);
+
+ llvm::Expected<Response> Handle(const Request &request);
+ void Handle(const Notification &notification);
+
+ llvm::Expected<Response> InitializeHandler(const Request &);
+
+ llvm::Expected<Response> ToolsListHandler(const Request &);
+ llvm::Expected<Response> ToolsCallHandler(const Request &);
+
+ llvm::Expected<Response> ResourcesListHandler(const Request &);
+ llvm::Expected<Response> ResourcesReadHandler(const Request &);
+
+ 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;
+
+ llvm::StringMap<RequestHandler> m_request_handlers;
+ llvm::StringMap<NotificationHandler> m_notification_handlers;
+};
+
+} // namespace lldb_protocol::mcp
+
+#endif
diff --git a/lldb/include/lldb/Protocol/MCP/Tool.h b/lldb/include/lldb/Protocol/MCP/Tool.h
index 96669d1..6c9f051 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/Symbol/Symbol.h b/lldb/include/lldb/Symbol/Symbol.h
index 688c8a5..b994c34 100644
--- a/lldb/include/lldb/Symbol/Symbol.h
+++ b/lldb/include/lldb/Symbol/Symbol.h
@@ -15,6 +15,7 @@
#include "lldb/Symbol/SymbolContextScope.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/UserID.h"
+#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-private.h"
#include "llvm/Support/JSON.h"
@@ -167,7 +168,7 @@ public:
lldb::SymbolType GetType() const { return (lldb::SymbolType)m_type; }
- void SetType(lldb::SymbolType type) { m_type = (lldb::SymbolType)type; }
+ void SetType(lldb::SymbolType type) { m_type = type; }
const char *GetTypeAsString() const;
@@ -301,6 +302,10 @@ public:
bool operator==(const Symbol &rhs) const;
+ static const char *GetTypeAsString(lldb::SymbolType symbol_type);
+
+ static lldb::SymbolType GetTypeFromString(const char *str);
+
protected:
// This is the internal guts of ResolveReExportedSymbol, it assumes
// reexport_name is not null, and that module_spec is valid. We track the
diff --git a/lldb/include/lldb/Target/CoreFileMemoryRanges.h b/lldb/include/lldb/Target/CoreFileMemoryRanges.h
index 78d01ac..ef56a02 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/ExecutionContext.h b/lldb/include/lldb/Target/ExecutionContext.h
index aebd0d5..f105e38 100644
--- a/lldb/include/lldb/Target/ExecutionContext.h
+++ b/lldb/include/lldb/Target/ExecutionContext.h
@@ -11,6 +11,7 @@
#include <mutex>
+#include "lldb/Host/ProcessRunLock.h"
#include "lldb/Target/StackID.h"
#include "lldb/lldb-private.h"
@@ -315,14 +316,6 @@ public:
ExecutionContext(const ExecutionContextRef *exe_ctx_ref,
bool thread_and_frame_only_if_stopped = false);
- // These two variants take in a locker, and grab the target, lock the API
- // mutex into locker, then fill in the rest of the shared pointers.
- ExecutionContext(const ExecutionContextRef &exe_ctx_ref,
- std::unique_lock<std::recursive_mutex> &locker)
- : ExecutionContext(&exe_ctx_ref, locker) {}
-
- ExecutionContext(const ExecutionContextRef *exe_ctx_ref,
- std::unique_lock<std::recursive_mutex> &locker);
// Create execution contexts from execution context scopes
ExecutionContext(ExecutionContextScope *exe_scope);
ExecutionContext(ExecutionContextScope &exe_scope);
@@ -566,6 +559,53 @@ protected:
lldb::StackFrameSP m_frame_sp; ///< The stack frame in thread.
};
+/// A wrapper class representing an execution context with non-null Target
+/// and Process pointers, a locked API mutex and a locked ProcessRunLock.
+/// The locks are private by design: to unlock them, destroy the
+/// StoppedExecutionContext.
+struct StoppedExecutionContext : ExecutionContext {
+ StoppedExecutionContext(lldb::TargetSP &target_sp,
+ lldb::ProcessSP &process_sp,
+ lldb::ThreadSP &thread_sp,
+ lldb::StackFrameSP &frame_sp,
+ std::unique_lock<std::recursive_mutex> api_lock,
+ ProcessRunLock::ProcessRunLocker stop_locker)
+ : m_api_lock(std::move(api_lock)), m_stop_locker(std::move(stop_locker)) {
+ assert(target_sp);
+ assert(process_sp);
+ assert(m_api_lock.owns_lock());
+ assert(m_stop_locker.IsLocked());
+ SetTargetSP(target_sp);
+ SetProcessSP(process_sp);
+ SetThreadSP(thread_sp);
+ SetFrameSP(frame_sp);
+ }
+
+ /// Transfers ownership of the locks from `other` to `this`, making `other`
+ /// unusable.
+ StoppedExecutionContext(StoppedExecutionContext &&other)
+ : StoppedExecutionContext(other.m_target_sp, other.m_process_sp,
+ other.m_thread_sp, other.m_frame_sp,
+ std::move(other.m_api_lock),
+ std::move(other.m_stop_locker)) {
+ other.Clear();
+ }
+
+ /// Clears this context, unlocking the ProcessRunLock and returning the
+ /// locked API lock, allowing callers to resume the process. Similar to
+ /// a move operation, this object is no longer usable.
+ [[nodiscard]] std::unique_lock<std::recursive_mutex> AllowResume();
+
+private:
+ std::unique_lock<std::recursive_mutex> m_api_lock;
+ ProcessRunLock::ProcessRunLocker m_stop_locker;
+};
+
+llvm::Expected<StoppedExecutionContext>
+GetStoppedExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr);
+llvm::Expected<StoppedExecutionContext>
+GetStoppedExecutionContext(const lldb::ExecutionContextRefSP &exe_ctx_ref_ptr);
+
} // namespace lldb_private
#endif // LLDB_TARGET_EXECUTIONCONTEXT_H
diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h
index 3f51c9a..d4104bf 100644
--- a/lldb/include/lldb/Target/StackFrame.h
+++ b/lldb/include/lldb/Target/StackFrame.h
@@ -241,8 +241,9 @@ public:
return m_reg_context_sp;
}
- /// Retrieve the list of variables that are in scope at this StackFrame's
- /// pc.
+ /// Retrieve the list of variables whose scope either:
+ /// * contains this StackFrame's pc,
+ /// * is a child of this StackFrame's current scope.
///
/// A frame that is not live may return an empty VariableList for a given
/// pc value even though variables would be available at this point if it
@@ -274,6 +275,9 @@ public:
/// that are visible to the entire compilation unit (e.g. file
/// static in C, globals that are homed in this CU).
///
+ /// \param[in] must_have_valid_location
+ /// Whether to filter variables whose location is not available at this
+ /// StackFrame's pc.
/// \return
/// A pointer to a list of variables.
lldb::VariableListSP
diff --git a/lldb/include/lldb/Target/StackID.h b/lldb/include/lldb/Target/StackID.h
index 0939279..fddbc8e 100644
--- a/lldb/include/lldb/Target/StackID.h
+++ b/lldb/include/lldb/Target/StackID.h
@@ -14,14 +14,15 @@
namespace lldb_private {
+class Process;
+
class StackID {
public:
// Constructors and Destructors
StackID() = default;
explicit StackID(lldb::addr_t pc, lldb::addr_t cfa,
- SymbolContextScope *symbol_scope)
- : m_pc(pc), m_cfa(cfa), m_symbol_scope(symbol_scope) {}
+ 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) {}
@@ -63,9 +64,8 @@ public:
protected:
friend class StackFrame;
- void SetPC(lldb::addr_t pc) { m_pc = pc; }
-
- void SetCFA(lldb::addr_t cfa) { m_cfa = cfa; }
+ 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
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 7b23c8a..14a09f2 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -18,6 +18,7 @@
#include "lldb/Breakpoint/BreakpointList.h"
#include "lldb/Breakpoint/BreakpointName.h"
#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Core/Address.h"
#include "lldb/Core/Architecture.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/ModuleList.h"
@@ -723,7 +724,7 @@ public:
lldb::BreakpointSP CreateBreakpoint(lldb::addr_t load_addr, bool internal,
bool request_hardware);
- // Use this to create a breakpoint from a load address and a module file spec
+ // Use this to create a breakpoint from a file address and a module file spec
lldb::BreakpointSP CreateAddressInModuleBreakpoint(lldb::addr_t file_addr,
bool internal,
const FileSpec &file_spec,
@@ -752,8 +753,8 @@ public:
const FileSpecList *containingModules,
const FileSpecList *containingSourceFiles, const char *func_name,
lldb::FunctionNameType func_name_type_mask, lldb::LanguageType language,
- lldb::addr_t offset, LazyBool skip_prologue, bool internal,
- bool request_hardware);
+ lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue,
+ bool internal, bool request_hardware);
lldb::BreakpointSP
CreateExceptionBreakpoint(enum lldb::LanguageType language, bool catch_bp,
@@ -1334,6 +1335,10 @@ public:
const lldb_private::RegisterFlags &flags,
uint32_t byte_size);
+ llvm::Expected<lldb::DisassemblerSP>
+ ReadInstructions(const Address &start_addr, uint32_t count,
+ const char *flavor_string = nullptr);
+
// Target Stop Hooks
class StopHook : public UserID {
public:
diff --git a/lldb/include/lldb/Utility/ArchSpec.h b/lldb/include/lldb/Utility/ArchSpec.h
index 7e9bc23..96bd5e3 100644
--- a/lldb/include/lldb/Utility/ArchSpec.h
+++ b/lldb/include/lldb/Utility/ArchSpec.h
@@ -564,6 +564,7 @@ protected:
/// \return true if \a lhs is less than \a rhs
bool operator<(const ArchSpec &lhs, const ArchSpec &rhs);
bool operator==(const ArchSpec &lhs, const ArchSpec &rhs);
+bool operator!=(const ArchSpec &lhs, const ArchSpec &rhs);
bool ParseMachCPUDashSubtypeTriple(llvm::StringRef triple_str, ArchSpec &arch);
diff --git a/lldb/include/lldb/Utility/Scalar.h b/lldb/include/lldb/Utility/Scalar.h
index b4b9c7e..dbb2609 100644
--- a/lldb/include/lldb/Utility/Scalar.h
+++ b/lldb/include/lldb/Utility/Scalar.h
@@ -84,11 +84,15 @@ public:
/// Store the binary representation of this value into the given storage.
/// Exactly GetByteSize() bytes will be stored, and the buffer must be large
/// enough to hold this data.
+ void GetBytes(uint8_t *storage, size_t length) const;
void GetBytes(llvm::MutableArrayRef<uint8_t> storage) const;
size_t GetByteSize() const;
- bool GetData(DataExtractor &data, size_t limit_byte_size = UINT32_MAX) const;
+ /// Get data with a byte size of GetByteSize().
+ bool GetData(DataExtractor &data) const;
+ /// Get data with a byte size forced to \p result_byte_size.
+ bool GetData(DataExtractor &data, size_t result_byte_size) const;
size_t GetAsMemoryData(void *dst, size_t dst_len,
lldb::ByteOrder dst_byte_order, Status &error) const;
diff --git a/lldb/include/lldb/Utility/StructuredData.h b/lldb/include/lldb/Utility/StructuredData.h
index 5e63ef9..59a2fd6 100644
--- a/lldb/include/lldb/Utility/StructuredData.h
+++ b/lldb/include/lldb/Utility/StructuredData.h
@@ -432,7 +432,7 @@ public:
}
return success;
}
-
+
template <class IntType>
bool GetValueForKeyAsInteger(llvm::StringRef key, IntType &result) const {
ObjectSP value_sp = GetValueForKey(key);
@@ -574,6 +574,30 @@ public:
void *m_object;
};
+ template <typename T> static ObjectSP FromInteger(T value) {
+ return std::make_shared<Integer<T>>(value);
+ }
+
+ static StructuredData::ObjectSP FromFloat(double value) {
+ return std::make_shared<StructuredData::Float>(value);
+ }
+ static StructuredData::ObjectSP FromBoolean(bool value) {
+ return std::make_shared<StructuredData::Boolean>(value);
+ }
+ static StructuredData::ObjectSP FromString(std::string value) {
+ return std::make_shared<StructuredData::String>(value);
+ }
+ static StructuredData::ObjectSP FromGeneric(void *value) {
+ return std::make_shared<StructuredData::Generic>(value);
+ }
+
+ static StructuredData::ObjectSP
+ FromKeyValue(llvm::StringRef key, const StructuredData::ObjectSP &value_sp) {
+ auto dict_sp = std::make_shared<StructuredData::Dictionary>();
+ dict_sp->AddItem(key, value_sp);
+ return dict_sp;
+ }
+
static ObjectSP ParseJSON(llvm::StringRef json_text);
static ObjectSP ParseJSONFromFile(const FileSpec &file, Status &error);
static bool IsRecordType(const ObjectSP object_sp);
diff --git a/lldb/include/lldb/Utility/XcodeSDK.h b/lldb/include/lldb/Utility/XcodeSDK.h
index a1a0ec4..5b345a4 100644
--- a/lldb/include/lldb/Utility/XcodeSDK.h
+++ b/lldb/include/lldb/Utility/XcodeSDK.h
@@ -86,8 +86,6 @@ public:
Type GetType() const;
llvm::StringRef GetString() const;
const FileSpec &GetSysroot() const;
- /// Whether this Xcode SDK supports Swift.
- bool SupportsSwift() const;
/// Whether LLDB feels confident importing Clang modules from this SDK.
static bool SDKSupportsModules(Type type, llvm::VersionTuple version);
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 709f063..1d10755 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 45e29b3..5a48c2c 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 9c1ba97..4345e6c 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 9eda7ba..90df109 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/ValueObject/ValueObject.h b/lldb/include/lldb/ValueObject/ValueObject.h
index 3c62f3c1..3f9f2b5 100644
--- a/lldb/include/lldb/ValueObject/ValueObject.h
+++ b/lldb/include/lldb/ValueObject/ValueObject.h
@@ -737,6 +737,12 @@ public:
CreateValueObjectFromAPFloat(lldb::TargetSP target, const llvm::APFloat &v,
CompilerType type, llvm::StringRef name);
+ /// Create a value object containing the given Scalar value.
+ static lldb::ValueObjectSP CreateValueObjectFromScalar(lldb::TargetSP target,
+ Scalar &s,
+ CompilerType type,
+ llvm::StringRef name);
+
/// Create a value object containing the given boolean value.
static lldb::ValueObjectSP CreateValueObjectFromBool(lldb::TargetSP target,
bool value,
diff --git a/lldb/include/lldb/ValueObject/ValueObjectConstResult.h b/lldb/include/lldb/ValueObject/ValueObjectConstResult.h
index 1e4b81c..6fbb153 100644
--- a/lldb/include/lldb/ValueObject/ValueObjectConstResult.h
+++ b/lldb/include/lldb/ValueObject/ValueObjectConstResult.h
@@ -60,6 +60,11 @@ public:
Value &value, ConstString name,
Module *module = nullptr);
+ static lldb::ValueObjectSP Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ Scalar &scalar, ConstString name,
+ Module *module = nullptr);
+
// When an expression fails to evaluate, we return an error
static lldb::ValueObjectSP Create(ExecutionContextScope *exe_scope,
Status &&error);
@@ -146,6 +151,12 @@ private:
ConstString name, Module *module = nullptr);
ValueObjectConstResult(ExecutionContextScope *exe_scope,
+ ValueObjectManager &manager,
+ const CompilerType &compiler_type,
+ const Scalar &scalar, ConstString name,
+ Module *module = nullptr);
+
+ ValueObjectConstResult(ExecutionContextScope *exe_scope,
ValueObjectManager &manager, Status &&error);
ValueObject *CreateChildAtIndex(size_t idx) override {
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index c63c1f0..fec9fde 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -777,6 +777,7 @@ enum SectionType {
eSectionTypeLLDBTypeSummaries,
eSectionTypeLLDBFormatters,
eSectionTypeSwiftModules,
+ eSectionTypeWasmName,
};
FLAGS_ENUM(EmulateInstructionOptions){
diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h
index 98c1e95..cc4657c 100644
--- a/lldb/include/lldb/lldb-private-enumerations.h
+++ b/lldb/include/lldb/lldb-private-enumerations.h
@@ -248,6 +248,22 @@ enum class IterationAction {
Stop,
};
+/// Specifies the type of PCs when creating a `HistoryThread`.
+/// - `Returns` - Usually, when LLDB unwinds the stack or we retrieve a stack
+/// trace via `backtrace()` we are collecting return addresses (except for the
+/// topmost frame which is the actual PC). LLDB then maps these return
+/// addresses back to call addresses to give accurate source line annotations.
+/// - `ReturnsNoZerothFrame` - Some trace providers (e.g., libsanitizers traces)
+/// collect return addresses but prune the topmost frames, so we should skip
+/// the special treatment of frame 0.
+/// - `Calls` - Other trace providers (e.g., ASan compiler-rt runtime) already
+/// perform this mapping, so we need to prevent LLDB from doing it again.
+enum class HistoryPCType {
+ Returns, ///< PCs are return addresses, except for topmost frame.
+ ReturnsNoZerothFrame, ///< All PCs are return addresses.
+ Calls ///< PCs are call addresses.
+};
+
inline std::string GetStatDescription(lldb_private::StatisticKind K) {
switch (K) {
case StatisticKind::ExpressionSuccessful:
diff --git a/lldb/packages/Python/lldbsuite/test/lldbutil.py b/lldb/packages/Python/lldbsuite/test/lldbutil.py
index 8112705..b8a78b7 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbutil.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbutil.py
@@ -1464,8 +1464,8 @@ class ChildVisitingFormatter(BasicFormatter):
return output.getvalue()
-class RecursiveDecentFormatter(BasicFormatter):
- """The recursive decent formatter prints the value and the decendents.
+class RecursiveDescentFormatter(BasicFormatter):
+ """The recursive descent formatter prints the value and the descendents.
The constructor takes two keyword args: indent_level, which defaults to 0,
and indent_child, which defaults to 2. The current indentation level is
@@ -1482,7 +1482,6 @@ class RecursiveDecentFormatter(BasicFormatter):
output = io.StringIO()
else:
output = buffer
-
BasicFormatter.format(self, value, buffer=output, indent=self.lindent)
new_indent = self.lindent + self.cindent
for child in value:
@@ -1490,7 +1489,7 @@ class RecursiveDecentFormatter(BasicFormatter):
BasicFormatter.format(self, child, buffer=output, indent=new_indent)
else:
if child.GetNumChildren() > 0:
- rdf = RecursiveDecentFormatter(indent_level=new_indent)
+ rdf = RecursiveDescentFormatter(indent_level=new_indent)
rdf.format(child, buffer=output)
else:
BasicFormatter.format(self, child, buffer=output, indent=new_indent)
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 35649b1..a1aa7e1 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
@@ -4,6 +4,14 @@
#include <type_traits>
#include <utility> // for std::forward
+// COMPRESSED_PAIR_REV versions:
+// 0 -> Post-c88580c layout
+// 1 -> Post-27c83382d83dc layout
+// 2 -> Post-769c42f4a552a layout
+// 3 -> Post-f5e687d7bf49c layout
+// 4 -> padding-less no_unique_address-based layout (introduced in
+// 27c83382d83dc)
+
namespace std {
namespace __lldb {
@@ -13,7 +21,50 @@ namespace __lldb {
#define _LLDB_NO_UNIQUE_ADDRESS [[__no_unique_address__]]
#endif
-#if COMPRESSED_PAIR_REV == 0 // Post-c88580c layout
+// From libc++ datasizeof.h
+template <class _Tp> struct _FirstPaddingByte {
+ _LLDB_NO_UNIQUE_ADDRESS _Tp __v_;
+ char __first_padding_byte_;
+};
+
+template <class _Tp>
+inline const size_t __datasizeof_v =
+ __builtin_offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);
+
+template <class _Tp>
+struct __lldb_is_final : public integral_constant<bool, __is_final(_Tp)> {};
+
+// The legacy layout has been patched, see
+// https://github.com/llvm/llvm-project/pull/142516.
+#if COMPRESSED_PAIR_REV == 1
+template <class _ToPad> class __compressed_pair_padding {
+ char __padding_[((is_empty<_ToPad>::value &&
+ !__lldb_is_final<_ToPad>::value) ||
+ is_reference<_ToPad>::value)
+ ? 0
+ : sizeof(_ToPad) - __datasizeof_v<_ToPad>];
+};
+#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) ||
+ sizeof(_ToPad) == __datasizeof_v<_ToPad>;
+
+template <class _Tp>
+inline const bool __is_reference_or_unpadded_object<_Tp &> = true;
+
+template <class _Tp>
+inline const bool __is_reference_or_unpadded_object<_Tp &&> = true;
+
+template <class _ToPad, bool _Empty = __is_reference_or_unpadded_object<_ToPad>>
+class __compressed_pair_padding {
+ char __padding_[sizeof(_ToPad) - __datasizeof_v<_ToPad>] = {};
+};
+
+template <class _ToPad> class __compressed_pair_padding<_ToPad, true> {};
+#endif // COMPRESSED_PAIR_REV == 1
+
+#if COMPRESSED_PAIR_REV == 0
struct __value_init_tag {};
struct __default_init_tag {};
@@ -59,49 +110,6 @@ public:
_T1 &first() { return static_cast<_Base1 &>(*this).__get(); }
};
#elif COMPRESSED_PAIR_REV == 1 || COMPRESSED_PAIR_REV == 2
-// From libc++ datasizeof.h
-template <class _Tp> struct _FirstPaddingByte {
- _LLDB_NO_UNIQUE_ADDRESS _Tp __v_;
- char __first_padding_byte_;
-};
-
-template <class _Tp>
-inline const size_t __datasizeof_v =
- __builtin_offsetof(_FirstPaddingByte<_Tp>, __first_padding_byte_);
-
-template <class _Tp>
-struct __lldb_is_final : public integral_constant<bool, __is_final(_Tp)> {};
-
-// The legacy layout has been patched, see
-// https://github.com/llvm/llvm-project/pull/142516.
-#if COMPRESSED_PAIR_REV == 1
-template <class _ToPad> class __compressed_pair_padding {
- char __padding_[((is_empty<_ToPad>::value &&
- !__lldb_is_final<_ToPad>::value) ||
- is_reference<_ToPad>::value)
- ? 0
- : sizeof(_ToPad) - __datasizeof_v<_ToPad>];
-};
-#else
-template <class _ToPad>
-inline const bool __is_reference_or_unpadded_object =
- (std::is_empty<_ToPad>::value && !__lldb_is_final<_ToPad>::value) ||
- sizeof(_ToPad) == __datasizeof_v<_ToPad>;
-
-template <class _Tp>
-inline const bool __is_reference_or_unpadded_object<_Tp &> = true;
-
-template <class _Tp>
-inline const bool __is_reference_or_unpadded_object<_Tp &&> = true;
-
-template <class _ToPad, bool _Empty = __is_reference_or_unpadded_object<_ToPad>>
-class __compressed_pair_padding {
- char __padding_[sizeof(_ToPad) - __datasizeof_v<_ToPad>] = {};
-};
-
-template <class _ToPad> class __compressed_pair_padding<_ToPad, true> {};
-#endif
-
#define _LLDB_COMPRESSED_PAIR(T1, Initializer1, T2, Initializer2) \
[[__gnu__::__aligned__( \
alignof(T2))]] _LLDB_NO_UNIQUE_ADDRESS T1 Initializer1; \
@@ -119,6 +127,27 @@ template <class _ToPad> class __compressed_pair_padding<_ToPad, true> {};
_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
@@ -127,7 +156,7 @@ template <class _ToPad> class __compressed_pair_padding<_ToPad, true> {};
_LLDB_NO_UNIQUE_ADDRESS T1 Name1; \
_LLDB_NO_UNIQUE_ADDRESS T2 Name2; \
_LLDB_NO_UNIQUE_ADDRESS T3 Name3
-#endif
+#endif // COMPRESSED_PAIR_REV == 3
} // namespace __lldb
} // namespace std
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 0b09893..0608ac3 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
@@ -12,15 +12,91 @@ import signal
import sys
import threading
import time
-from typing import Any, Optional, Union, BinaryIO, TextIO
+from typing import (
+ Any,
+ Optional,
+ Dict,
+ cast,
+ List,
+ Callable,
+ IO,
+ Union,
+ BinaryIO,
+ TextIO,
+ TypedDict,
+ Literal,
+)
## DAP type references
-Event = dict[str, Any]
-Request = dict[str, Any]
-Response = dict[str, Any]
+
+
+class Event(TypedDict):
+ type: Literal["event"]
+ seq: int
+ event: str
+ body: Any
+
+
+class Request(TypedDict, total=False):
+ type: Literal["request"]
+ seq: int
+ command: str
+ arguments: Any
+
+
+class Response(TypedDict):
+ type: Literal["response"]
+ seq: int
+ request_seq: int
+ success: bool
+ command: str
+ message: Optional[str]
+ body: Any
+
+
ProtocolMessage = Union[Event, Request, Response]
+class Source(TypedDict, total=False):
+ name: str
+ path: str
+ sourceReference: int
+
+ @staticmethod
+ def build(
+ *,
+ name: Optional[str] = None,
+ path: Optional[str] = None,
+ source_reference: Optional[int] = None,
+ ) -> "Source":
+ """Builds a source from the given name, path or source_reference."""
+ if not name and not path and not source_reference:
+ raise ValueError(
+ "Source.build requires either name, path, or source_reference"
+ )
+
+ s = Source()
+ if name:
+ s["name"] = name
+ if path:
+ if not name:
+ s["name"] = os.path.basename(path)
+ s["path"] = path
+ if source_reference is not None:
+ s["sourceReference"] = source_reference
+ return s
+
+
+class Breakpoint(TypedDict, total=False):
+ id: int
+ verified: bool
+ source: Source
+
+ @staticmethod
+ def is_verified(src: "Breakpoint") -> bool:
+ return src.get("verified", False)
+
+
def dump_memory(base_addr, data, num_per_line, outfile):
data_len = len(data)
hex_string = binascii.hexlify(data)
@@ -58,7 +134,9 @@ def dump_memory(base_addr, data, num_per_line, outfile):
outfile.write("\n")
-def read_packet(f, verbose=False, trace_file=None):
+def read_packet(
+ f: IO[bytes], trace_file: Optional[IO[str]] = None
+) -> Optional[ProtocolMessage]:
"""Decode a JSON packet that starts with the content length and is
followed by the JSON bytes from a file 'f'. Returns None on EOF.
"""
@@ -70,19 +148,13 @@ def read_packet(f, verbose=False, trace_file=None):
prefix = "Content-Length: "
if line.startswith(prefix):
# Decode length of JSON bytes
- if verbose:
- print('content: "%s"' % (line))
length = int(line[len(prefix) :])
- if verbose:
- print('length: "%u"' % (length))
# Skip empty line
- line = f.readline()
- if verbose:
- print('empty: "%s"' % (line))
+ separator = f.readline().decode()
+ if separator != "":
+ Exception("malformed DAP content header, unexpected line: " + separator)
# Read JSON bytes
- json_str = f.read(length)
- if verbose:
- print('json: "%s"' % (json_str))
+ json_str = f.read(length).decode()
if trace_file:
trace_file.write("from adapter:\n%s\n" % (json_str))
# Decode the JSON bytes into a python dictionary
@@ -95,7 +167,7 @@ def packet_type_is(packet, packet_type):
return "type" in packet and packet["type"] == packet_type
-def dump_dap_log(log_file):
+def dump_dap_log(log_file: Optional[str]) -> None:
print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr)
if log_file is None:
print("no log file available", file=sys.stderr)
@@ -105,36 +177,6 @@ def dump_dap_log(log_file):
print("========= END =========", file=sys.stderr)
-class Source(object):
- def __init__(
- self, path: Optional[str] = None, source_reference: Optional[int] = None
- ):
- self._name = None
- self._path = None
- self._source_reference = None
-
- if path is not None:
- self._name = os.path.basename(path)
- self._path = path
- elif source_reference is not None:
- self._source_reference = source_reference
- else:
- raise ValueError("Either path or source_reference must be provided")
-
- def __str__(self):
- return f"Source(name={self.name}, path={self.path}), source_reference={self.source_reference})"
-
- def as_dict(self):
- source_dict = {}
- if self._name is not None:
- source_dict["name"] = self._name
- if self._path is not None:
- source_dict["path"] = self._path
- if self._source_reference is not None:
- source_dict["sourceReference"] = self._source_reference
- return source_dict
-
-
class NotSupportedError(KeyError):
"""Raised if a feature is not supported due to its capabilities."""
@@ -152,25 +194,41 @@ class DebugCommunication(object):
self.log_file = log_file
self.send = send
self.recv = recv
- self.recv_packets: list[Optional[ProtocolMessage]] = []
- self.recv_condition = threading.Condition()
- self.recv_thread = threading.Thread(target=self._read_packet_thread)
- self.process_event_body = None
- self.exit_status: Optional[int] = None
- self.capabilities: dict[str, Any] = {}
- self.progress_events: list[Event] = []
- self.reverse_requests = []
- self.sequence = 1
- self.threads = None
- self.thread_stop_reasons = {}
- self.recv_thread.start()
- self.output_condition = threading.Condition()
- self.output: dict[str, list[str]] = {}
- self.configuration_done_sent = False
- self.initialized = False
- self.frame_scopes = {}
+
+ # Packets that have been received and processed but have not yet been
+ # requested by a test case.
+ self._pending_packets: List[Optional[ProtocolMessage]] = []
+ # Received packets that have not yet been processed.
+ self._recv_packets: List[Optional[ProtocolMessage]] = []
+ # Used as a mutex for _recv_packets and for notify when _recv_packets
+ # changes.
+ self._recv_condition = threading.Condition()
+ self._recv_thread = threading.Thread(target=self._read_packet_thread)
+
+ # session state
self.init_commands = init_commands
- self.resolved_breakpoints = {}
+ self.exit_status: Optional[int] = None
+ self.capabilities: Dict = {}
+ self.initialized: bool = False
+ self.configuration_done_sent: bool = False
+ self.process_event_body: Optional[Dict] = None
+ self.terminated: bool = False
+ self.events: List[Event] = []
+ self.progress_events: List[Event] = []
+ self.reverse_requests: List[Request] = []
+ self.module_events: List[Dict] = []
+ self.sequence: int = 1
+ self.output: Dict[str, str] = {}
+
+ # debuggee state
+ self.threads: Optional[dict] = None
+ self.thread_stop_reasons: Dict[str, Any] = {}
+ self.frame_scopes: Dict[str, Any] = {}
+ # keyed by breakpoint id
+ self.resolved_breakpoints: dict[str, Breakpoint] = {}
+
+ # trigger enqueue thread
+ self._recv_thread.start()
@classmethod
def encode_content(cls, s: str) -> bytes:
@@ -188,267 +246,324 @@ class DebugCommunication(object):
)
def _read_packet_thread(self):
- done = False
try:
- while not done:
+ while True:
packet = read_packet(self.recv, trace_file=self.trace_file)
# `packet` will be `None` on EOF. We want to pass it down to
# handle_recv_packet anyway so the main thread can handle unexpected
# termination of lldb-dap and stop waiting for new packets.
- done = not self._handle_recv_packet(packet)
+ if not self._handle_recv_packet(packet):
+ break
finally:
dump_dap_log(self.log_file)
- def get_modules(self, startModule: int = 0, moduleCount: int = 0):
- module_list = self.request_modules(startModule, moduleCount)["body"]["modules"]
+ def get_modules(
+ self, start_module: Optional[int] = None, module_count: Optional[int] = None
+ ) -> Dict:
+ resp = self.request_modules(start_module, module_count)
+ if not resp["success"]:
+ raise ValueError(f"request_modules failed: {resp!r}")
modules = {}
+ module_list = resp["body"]["modules"]
for module in module_list:
modules[module["name"]] = module
return modules
- def get_output(self, category, timeout=0.0, clear=True):
- self.output_condition.acquire()
- output = None
+ def get_output(self, category: str, clear=True) -> str:
+ output = ""
if category in self.output:
- output = self.output[category]
+ output = self.output.get(category, "")
if clear:
del self.output[category]
- elif timeout != 0.0:
- self.output_condition.wait(timeout)
- if category in self.output:
- output = self.output[category]
- if clear:
- del self.output[category]
- self.output_condition.release()
return output
- def collect_output(self, category, timeout_secs, pattern, clear=True):
- end_time = time.time() + timeout_secs
- collected_output = ""
- while end_time > time.time():
- output = self.get_output(category, timeout=0.25, clear=clear)
- if output:
- collected_output += output
- if pattern is not None and pattern in output:
- break
- return collected_output if collected_output else None
+ def collect_output(
+ self,
+ category: str,
+ timeout: float,
+ pattern: Optional[str] = None,
+ clear=True,
+ ) -> str:
+ """Collect output from 'output' events.
+ Args:
+ category: The category to collect.
+ timeout: The max duration for collecting output.
+ pattern:
+ Optional, if set, return once this pattern is detected in the
+ collected output.
+ Returns:
+ The collected output.
+ """
+ deadline = time.monotonic() + timeout
+ output = self.get_output(category, clear)
+ while deadline >= time.monotonic() and (
+ pattern is None or pattern not in output
+ ):
+ event = self.wait_for_event(["output"], timeout=deadline - time.monotonic())
+ if not event: # Timeout or EOF
+ break
+ output += self.get_output(category, clear=clear)
+ return output
def _enqueue_recv_packet(self, packet: Optional[ProtocolMessage]):
- self.recv_condition.acquire()
- self.recv_packets.append(packet)
- self.recv_condition.notify()
- self.recv_condition.release()
+ with self.recv_condition:
+ self.recv_packets.append(packet)
+ self.recv_condition.notify()
def _handle_recv_packet(self, packet: Optional[ProtocolMessage]) -> bool:
- """Called by the read thread that is waiting for all incoming packets
- to store the incoming packet in "self.recv_packets" in a thread safe
- way. This function will then signal the "self.recv_condition" to
- indicate a new packet is available. Returns True if the caller
- should keep calling this function for more packets.
+ """Handles an incoming packet.
+
+ Called by the read thread that is waiting for all incoming packets
+ to store the incoming packet in "self._recv_packets" in a thread safe
+ way. This function will then signal the "self._recv_condition" to
+ indicate a new packet is available.
+
+ Args:
+ packet: A new packet to store.
+
+ Returns:
+ True if the caller should keep calling this function for more
+ packets.
"""
- # If EOF, notify the read thread by enqueuing a None.
- if not packet:
- self._enqueue_recv_packet(None)
- return False
-
- # Check the packet to see if is an event packet
- keepGoing = True
- packet_type = packet["type"]
- if packet_type == "event":
- event = packet["event"]
- body = None
- if "body" in packet:
- body = packet["body"]
- # Handle the event packet and cache information from these packets
- # as they come in
- if event == "output":
- # Store any output we receive so clients can retrieve it later.
- category = body["category"]
- output = body["output"]
- self.output_condition.acquire()
- if category in self.output:
- self.output[category] += output
- else:
- self.output[category] = output
- self.output_condition.notify()
- self.output_condition.release()
- # no need to add 'output' event packets to our packets list
- return keepGoing
- elif event == "initialized":
- self.initialized = True
- elif event == "process":
- # When a new process is attached or launched, remember the
- # details that are available in the body of the event
- self.process_event_body = body
- elif event == "exited":
- # Process exited, mark the status to indicate the process is not
- # alive.
- self.exit_status = body["exitCode"]
- elif event == "continued":
- # When the process continues, clear the known threads and
- # thread_stop_reasons.
- all_threads_continued = body.get("allThreadsContinued", True)
- tid = body["threadId"]
- if tid in self.thread_stop_reasons:
- del self.thread_stop_reasons[tid]
- self._process_continued(all_threads_continued)
- elif event == "stopped":
- # Each thread that stops with a reason will send a
- # 'stopped' event. We need to remember the thread stop
- # reasons since the 'threads' command doesn't return
- # that information.
- self._process_stopped()
- tid = body["threadId"]
- self.thread_stop_reasons[tid] = body
- elif event.startswith("progress"):
- # Progress events come in as 'progressStart', 'progressUpdate',
- # and 'progressEnd' events. Keep these around in case test
- # cases want to verify them.
- self.progress_events.append(packet)
- elif event == "breakpoint":
- # Breakpoint events are sent when a breakpoint is resolved
- self._update_verified_breakpoints([body["breakpoint"]])
- elif event == "capabilities":
- # Update the capabilities with new ones from the event.
- self.capabilities.update(body["capabilities"])
-
- elif packet_type == "response":
- if packet["command"] == "disconnect":
- keepGoing = False
- self._enqueue_recv_packet(packet)
- return keepGoing
+ with self._recv_condition:
+ self._recv_packets.append(packet)
+ self._recv_condition.notify()
+ # packet is None on EOF
+ return packet is not None and not (
+ packet["type"] == "response" and packet["command"] == "disconnect"
+ )
+
+ def _recv_packet(
+ self,
+ *,
+ predicate: Optional[Callable[[ProtocolMessage], bool]] = None,
+ timeout: Optional[float] = None,
+ ) -> Optional[ProtocolMessage]:
+ """Processes received packets from the adapter.
+ Updates the DebugCommunication stateful properties based on the received
+ packets in the order they are received.
+ NOTE: The only time the session state properties should be updated is
+ during this call to ensure consistency during tests.
+ Args:
+ predicate:
+ Optional, if specified, returns the first packet that matches
+ the given predicate.
+ timeout:
+ Optional, if specified, processes packets until either the
+ timeout occurs or the predicate matches a packet, whichever
+ occurs first.
+ Returns:
+ The first matching packet for the given predicate, if specified,
+ otherwise None.
+ """
+ assert (
+ threading.current_thread != self._recv_thread
+ ), "Must not be called from the _recv_thread"
+
+ def process_until_match():
+ self._process_recv_packets()
+ for i, packet in enumerate(self._pending_packets):
+ if packet is None:
+ # We need to return a truthy value to break out of the
+ # wait_for, use `EOFError` as an indicator of EOF.
+ return EOFError()
+ if predicate and predicate(packet):
+ self._pending_packets.pop(i)
+ return packet
+
+ with self._recv_condition:
+ packet = self._recv_condition.wait_for(process_until_match, timeout)
+ return None if isinstance(packet, EOFError) else packet
+
+ def _process_recv_packets(self) -> None:
+ """Process received packets, updating the session state."""
+ with self._recv_condition:
+ for packet in self._recv_packets:
+ # Handle events that may modify any stateful properties of
+ # the DAP session.
+ if packet and packet["type"] == "event":
+ self._handle_event(packet)
+ elif packet and packet["type"] == "request":
+ # Handle reverse requests and keep processing.
+ self._handle_reverse_request(packet)
+ # Move the packet to the pending queue.
+ self._pending_packets.append(packet)
+ self._recv_packets.clear()
+
+ def _handle_event(self, packet: Event) -> None:
+ """Handle any events that modify debug session state we track."""
+ event = packet["event"]
+ body: Optional[Dict] = packet.get("body", None)
+
+ if event == "output" and body:
+ # Store any output we receive so clients can retrieve it later.
+ category = body["category"]
+ output = body["output"]
+ if category in self.output:
+ self.output[category] += output
+ else:
+ self.output[category] = output
+ elif event == "initialized":
+ self.initialized = True
+ elif event == "process":
+ # When a new process is attached or launched, remember the
+ # details that are available in the body of the event
+ self.process_event_body = body
+ elif event == "exited" and body:
+ # Process exited, mark the status to indicate the process is not
+ # alive.
+ self.exit_status = body["exitCode"]
+ elif event == "continued" and body:
+ # When the process continues, clear the known threads and
+ # thread_stop_reasons.
+ all_threads_continued = body.get("allThreadsContinued", True)
+ tid = body["threadId"]
+ if tid in self.thread_stop_reasons:
+ del self.thread_stop_reasons[tid]
+ self._process_continued(all_threads_continued)
+ elif event == "stopped" and body:
+ # Each thread that stops with a reason will send a
+ # 'stopped' event. We need to remember the thread stop
+ # reasons since the 'threads' command doesn't return
+ # that information.
+ self._process_stopped()
+ tid = body["threadId"]
+ self.thread_stop_reasons[tid] = body
+ elif event.startswith("progress"):
+ # Progress events come in as 'progressStart', 'progressUpdate',
+ # and 'progressEnd' events. Keep these around in case test
+ # cases want to verify them.
+ self.progress_events.append(packet)
+ elif event == "breakpoint" and body:
+ # Breakpoint events are sent when a breakpoint is resolved
+ self._update_verified_breakpoints([body["breakpoint"]])
+ elif event == "capabilities" and body:
+ # Update the capabilities with new ones from the event.
+ self.capabilities.update(body["capabilities"])
+
+ def _handle_reverse_request(self, request: Request) -> None:
+ if request in self.reverse_requests:
+ return
+ self.reverse_requests.append(request)
+ arguments = request.get("arguments")
+ if request["command"] == "runInTerminal" and arguments is not None:
+ in_shell = arguments.get("argsCanBeInterpretedByShell", False)
+ print("spawning...", arguments["args"])
+ proc = subprocess.Popen(
+ arguments["args"],
+ env=arguments.get("env", {}),
+ cwd=arguments.get("cwd", None),
+ stdin=subprocess.DEVNULL,
+ stdout=sys.stderr,
+ stderr=sys.stderr,
+ shell=in_shell,
+ )
+ body = {}
+ if in_shell:
+ body["shellProcessId"] = proc.pid
+ else:
+ body["processId"] = proc.pid
+ self.send_packet(
+ {
+ "type": "response",
+ "seq": 0,
+ "request_seq": request["seq"],
+ "success": True,
+ "command": "runInTerminal",
+ "body": body,
+ }
+ )
+ elif request["command"] == "startDebugging":
+ self.send_packet(
+ {
+ "type": "response",
+ "seq": 0,
+ "request_seq": request["seq"],
+ "success": True,
+ "message": None,
+ "command": "startDebugging",
+ "body": {},
+ }
+ )
+ else:
+ desc = 'unknown reverse request "%s"' % (request["command"])
+ raise ValueError(desc)
def _process_continued(self, all_threads_continued: bool):
self.frame_scopes = {}
if all_threads_continued:
self.thread_stop_reasons = {}
- def _update_verified_breakpoints(self, breakpoints: list[Event]):
- for breakpoint in breakpoints:
- if "id" in breakpoint:
- self.resolved_breakpoints[str(breakpoint["id"])] = breakpoint.get(
- "verified", False
- )
+ def _update_verified_breakpoints(self, breakpoints: list[Breakpoint]):
+ for bp in breakpoints:
+ # If no id is set, we cannot correlate the given breakpoint across
+ # requests, ignore it.
+ if "id" not in bp:
+ continue
+
+ self.resolved_breakpoints[str(bp["id"])] = bp
+
+ def send_packet(self, packet: ProtocolMessage) -> int:
+ """Takes a dictionary representation of a DAP request and send the request to the debug adapter.
- def send_packet(self, command_dict: Request, set_sequence=True):
- """Take the "command_dict" python dictionary and encode it as a JSON
- string and send the contents as a packet to the VSCode debug
- adapter"""
- # Set the sequence ID for this command automatically
- if set_sequence:
- command_dict["seq"] = self.sequence
+ Returns the seq number of the request.
+ """
+ # Set the seq for requests.
+ if packet["type"] == "request":
+ packet["seq"] = self.sequence
self.sequence += 1
+ else:
+ packet["seq"] = 0
+
# Encode our command dictionary as a JSON string
- json_str = json.dumps(command_dict, separators=(",", ":"))
+ json_str = json.dumps(packet, separators=(",", ":"))
+
if self.trace_file:
self.trace_file.write("to adapter:\n%s\n" % (json_str))
+
length = len(json_str)
if length > 0:
# Send the encoded JSON packet and flush the 'send' file
self.send.write(self.encode_content(json_str))
self.send.flush()
- def recv_packet(
- self,
- filter_type: Optional[str] = None,
- filter_event: Optional[Union[str, list[str]]] = None,
- timeout: Optional[float] = None,
- ) -> Optional[ProtocolMessage]:
- """Get a JSON packet from the VSCode debug adapter. This function
- assumes a thread that reads packets is running and will deliver
- any received packets by calling handle_recv_packet(...). This
- function will wait for the packet to arrive and return it when
- it does."""
- while True:
- try:
- self.recv_condition.acquire()
- packet = None
- while True:
- for i, curr_packet in enumerate(self.recv_packets):
- if not curr_packet:
- raise EOFError
- packet_type = curr_packet["type"]
- if filter_type is None or packet_type in filter_type:
- if filter_event is None or (
- packet_type == "event"
- and curr_packet["event"] in filter_event
- ):
- packet = self.recv_packets.pop(i)
- break
- if packet:
- break
- # Sleep until packet is received
- len_before = len(self.recv_packets)
- self.recv_condition.wait(timeout)
- len_after = len(self.recv_packets)
- if len_before == len_after:
- return None # Timed out
- return packet
- except EOFError:
- return None
- finally:
- self.recv_condition.release()
-
- def send_recv(self, command):
+ return packet["seq"]
+
+ def _send_recv(self, request: Request) -> Optional[Response]:
"""Send a command python dictionary as JSON and receive the JSON
response. Validates that the response is the correct sequence and
command in the reply. Any events that are received are added to the
events list in this object"""
- self.send_packet(command)
- done = False
- while not done:
- response_or_request = self.recv_packet(filter_type=["response", "request"])
- if response_or_request is None:
- desc = 'no response for "%s"' % (command["command"])
- raise ValueError(desc)
- if response_or_request["type"] == "response":
- self.validate_response(command, response_or_request)
- return response_or_request
- else:
- self.reverse_requests.append(response_or_request)
- if response_or_request["command"] == "runInTerminal":
- subprocess.Popen(
- response_or_request["arguments"].get("args"),
- env=response_or_request["arguments"].get("env", {}),
- )
- self.send_packet(
- {
- "type": "response",
- "request_seq": response_or_request["seq"],
- "success": True,
- "command": "runInTerminal",
- "body": {},
- },
- )
- elif response_or_request["command"] == "startDebugging":
- self.send_packet(
- {
- "type": "response",
- "request_seq": response_or_request["seq"],
- "success": True,
- "command": "startDebugging",
- "body": {},
- },
- )
- else:
- desc = 'unknown reverse request "%s"' % (
- response_or_request["command"]
- )
- raise ValueError(desc)
+ seq = self.send_packet(request)
+ response = self.receive_response(seq)
+ if response is None:
+ raise ValueError(f"no response for {request!r}")
+ self.validate_response(request, response)
+ return response
- return None
+ def receive_response(self, seq: int) -> Optional[Response]:
+ """Waits for a response with the associated request_sec."""
+
+ def predicate(p: ProtocolMessage):
+ return p["type"] == "response" and p["request_seq"] == seq
+
+ return cast(Optional[Response], self._recv_packet(predicate=predicate))
def wait_for_event(
- self, filter: Union[str, list[str]], timeout: Optional[float] = None
+ self, filter: List[str] = [], timeout: Optional[float] = None
) -> Optional[Event]:
"""Wait for the first event that matches the filter."""
- return self.recv_packet(
- filter_type="event", filter_event=filter, timeout=timeout
+
+ def predicate(p: ProtocolMessage):
+ return p["type"] == "event" and p["event"] in filter
+
+ return cast(
+ Optional[Event], self._recv_packet(predicate=predicate, timeout=timeout)
)
def wait_for_stopped(
self, timeout: Optional[float] = None
- ) -> Optional[list[Event]]:
+ ) -> Optional[List[Event]]:
stopped_events = []
stopped_event = self.wait_for_event(
filter=["stopped", "exited"], timeout=timeout
@@ -469,7 +584,7 @@ class DebugCommunication(object):
def wait_for_breakpoint_events(self, timeout: Optional[float] = None):
breakpoint_events: list[Event] = []
while True:
- event = self.wait_for_event("breakpoint", timeout=timeout)
+ event = self.wait_for_event(["breakpoint"], timeout=timeout)
if not event:
break
breakpoint_events.append(event)
@@ -480,20 +595,27 @@ class DebugCommunication(object):
):
"""Wait for all breakpoints to be verified. Return all unverified breakpoints."""
while any(id not in self.resolved_breakpoints for id in breakpoint_ids):
- breakpoint_event = self.wait_for_event("breakpoint", timeout=timeout)
+ breakpoint_event = self.wait_for_event(["breakpoint"], timeout=timeout)
if breakpoint_event is None:
break
- return [id for id in breakpoint_ids if id not in self.resolved_breakpoints]
+ return [
+ id
+ for id in breakpoint_ids
+ if (
+ id not in self.resolved_breakpoints
+ or not Breakpoint.is_verified(self.resolved_breakpoints[id])
+ )
+ ]
def wait_for_exited(self, timeout: Optional[float] = None):
- event_dict = self.wait_for_event("exited", timeout=timeout)
+ event_dict = self.wait_for_event(["exited"], timeout=timeout)
if event_dict is None:
raise ValueError("didn't get exited event")
return event_dict
def wait_for_terminated(self, timeout: Optional[float] = None):
- event_dict = self.wait_for_event("terminated", timeout)
+ event_dict = self.wait_for_event(["terminated"], timeout)
if event_dict is None:
raise ValueError("didn't get terminated event")
return event_dict
@@ -704,7 +826,7 @@ class DebugCommunication(object):
if gdbRemoteHostname is not None:
args_dict["gdb-remote-hostname"] = gdbRemoteHostname
command_dict = {"command": "attach", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_breakpointLocations(
self, file_path, line, end_line=None, column=None, end_column=None
@@ -726,7 +848,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_configurationDone(self):
command_dict = {
@@ -734,7 +856,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": {},
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if response:
self.configuration_done_sent = True
self.request_threads()
@@ -763,7 +885,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if response["success"]:
self._process_continued(response["body"]["allThreadsContinued"])
# Caller must still call wait_for_stopped.
@@ -780,7 +902,7 @@ class DebugCommunication(object):
if restartArguments:
command_dict["arguments"] = restartArguments
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
# Caller must still call wait_for_stopped.
return response
@@ -796,7 +918,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_disassemble(
self,
@@ -816,7 +938,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)["body"]["instructions"]
+ return self._send_recv(command_dict)["body"]["instructions"]
def request_readMemory(self, memoryReference, offset, count):
args_dict = {
@@ -829,7 +951,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_writeMemory(self, memoryReference, data, offset=0, allowPartial=False):
args_dict = {
@@ -847,7 +969,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None):
stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
@@ -863,7 +985,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_exceptionInfo(self, threadId=None):
if threadId is None:
@@ -874,7 +996,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_initialize(self, sourceInitFile=False):
command_dict = {
@@ -895,10 +1017,10 @@ class DebugCommunication(object):
"$__lldb_sourceInitFile": sourceInitFile,
},
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if response:
if "body" in response:
- self.capabilities = response["body"]
+ self.capabilities.update(response.get("body", {}))
return response
def request_launch(
@@ -978,14 +1100,14 @@ class DebugCommunication(object):
if commandEscapePrefix is not None:
args_dict["commandEscapePrefix"] = commandEscapePrefix
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_next(self, threadId, granularity="statement"):
if self.exit_status is not None:
raise ValueError("request_continue called after process exited")
args_dict = {"threadId": threadId, "granularity": granularity}
command_dict = {"command": "next", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_stepIn(self, threadId, targetId, granularity="statement"):
if self.exit_status is not None:
@@ -998,7 +1120,7 @@ class DebugCommunication(object):
"granularity": granularity,
}
command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_stepInTargets(self, frameId):
if self.exit_status is not None:
@@ -1010,14 +1132,14 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_stepOut(self, threadId):
if self.exit_status is not None:
raise ValueError("request_stepOut called after process exited")
args_dict = {"threadId": threadId}
command_dict = {"command": "stepOut", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_pause(self, threadId=None):
if self.exit_status is not None:
@@ -1026,12 +1148,12 @@ class DebugCommunication(object):
threadId = self.get_thread_id()
args_dict = {"threadId": threadId}
command_dict = {"command": "pause", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_scopes(self, frameId):
args_dict = {"frameId": frameId}
command_dict = {"command": "scopes", "type": "request", "arguments": args_dict}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_setBreakpoints(self, source: Source, line_array, data=None):
"""data is array of parameters for breakpoints in line_array.
@@ -1039,7 +1161,7 @@ class DebugCommunication(object):
It contains optional location/hitCondition/logMessage parameters.
"""
args_dict = {
- "source": source.as_dict(),
+ "source": source,
"sourceModified": False,
}
if line_array is not None:
@@ -1067,7 +1189,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if response["success"]:
self._update_verified_breakpoints(response["body"]["breakpoints"])
return response
@@ -1083,7 +1205,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_setFunctionBreakpoints(self, names, condition=None, hitCondition=None):
breakpoints = []
@@ -1100,7 +1222,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if response["success"]:
self._update_verified_breakpoints(response["body"]["breakpoints"])
return response
@@ -1121,7 +1243,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_setDataBreakpoint(self, dataBreakpoints):
"""dataBreakpoints is a list of dictionary with following fields:
@@ -1138,7 +1260,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_compileUnits(self, moduleId):
args_dict = {"moduleId": moduleId}
@@ -1147,7 +1269,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
return response
def request_completions(self, text, frameId=None):
@@ -1159,17 +1281,43 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
-
- def request_modules(self, startModule: int, moduleCount: int):
- return self.send_recv(
- {
- "command": "modules",
- "type": "request",
- "arguments": {"startModule": startModule, "moduleCount": moduleCount},
- }
+ return self._send_recv(command_dict)
+
+ def request_modules(
+ self,
+ start_module: Optional[int] = None,
+ module_count: Optional[int] = None,
+ ):
+ args_dict = {}
+
+ if start_module is not None:
+ args_dict["startModule"] = start_module
+ if module_count is not None:
+ args_dict["moduleCount"] = module_count
+
+ return self._send_recv(
+ {"command": "modules", "type": "request", "arguments": args_dict}
)
+ def request_moduleSymbols(
+ self,
+ moduleId: str = "",
+ moduleName: str = "",
+ startIndex: int = 0,
+ count: int = 0,
+ ):
+ command_dict = {
+ "command": "__lldb_moduleSymbols",
+ "type": "request",
+ "arguments": {
+ "moduleId": moduleId,
+ "moduleName": moduleName,
+ "startIndex": startIndex,
+ "count": count,
+ },
+ }
+ return self._send_recv(command_dict)
+
def request_stackTrace(
self, threadId=None, startFrame=None, levels=None, format=None, dump=False
):
@@ -1187,7 +1335,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if dump:
for idx, frame in enumerate(response["body"]["stackFrames"]):
name = frame["name"]
@@ -1202,18 +1350,30 @@ class DebugCommunication(object):
print("[%3u] %s" % (idx, name))
return response
- def request_source(self, sourceReference):
+ def request_source(
+ self, *, source: Optional[Source] = None, sourceReference: Optional[int] = None
+ ):
"""Request a source from a 'Source' reference."""
+ if source is None and sourceReference is None:
+ raise ValueError("request_source requires either source or sourceReference")
+ elif source is not None:
+ sourceReference = source["sourceReference"]
+ elif sourceReference is not None:
+ source = {"sourceReference": sourceReference}
+ else:
+ raise ValueError(
+ "request_source requires either source or sourceReference not both"
+ )
command_dict = {
"command": "source",
"type": "request",
"arguments": {
- "source": {"sourceReference": sourceReference},
+ "source": source,
# legacy version of the request
"sourceReference": sourceReference,
},
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_threads(self):
"""Request a list of all threads and combine any information from any
@@ -1221,7 +1381,7 @@ class DebugCommunication(object):
thread actually stopped. Returns an array of thread dictionaries
with information about all threads"""
command_dict = {"command": "threads", "type": "request", "arguments": {}}
- response = self.send_recv(command_dict)
+ response = self._send_recv(command_dict)
if not response["success"]:
self.threads = None
return response
@@ -1261,7 +1421,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_setVariable(self, containingVarRef, name, value, id=None):
args_dict = {
@@ -1276,7 +1436,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_locations(self, locationReference):
args_dict = {
@@ -1287,7 +1447,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def request_testGetTargetBreakpoints(self):
"""A request packet used in the LLDB test suite to get all currently
@@ -1299,12 +1459,12 @@ class DebugCommunication(object):
"type": "request",
"arguments": {},
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
def terminate(self):
self.send.close()
- if self.recv_thread.is_alive():
- self.recv_thread.join()
+ if self._recv_thread.is_alive():
+ self._recv_thread.join()
def request_setInstructionBreakpoints(self, memory_reference=[]):
breakpoints = []
@@ -1319,7 +1479,7 @@ class DebugCommunication(object):
"type": "request",
"arguments": args_dict,
}
- return self.send_recv(command_dict)
+ return self._send_recv(command_dict)
class DebugAdapterServer(DebugCommunication):
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 1567462..b28a787 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
@@ -1,6 +1,6 @@
import os
import time
-from typing import Optional
+from typing import Optional, Callable, Any, List, Union
import uuid
import dap_server
@@ -59,24 +59,25 @@ class DAPTestCaseBase(TestBase):
Each object in data is 1:1 mapping with the entry in lines.
It contains optional location/hitCondition/logMessage parameters.
"""
- response = self.dap_server.request_setBreakpoints(
- Source(source_path), lines, data
+ return self.set_source_breakpoints_from_source(
+ Source(path=source_path), lines, data, wait_for_resolve
)
- if response is None or not response["success"]:
- return []
- breakpoints = response["body"]["breakpoints"]
- breakpoint_ids = []
- for breakpoint in breakpoints:
- breakpoint_ids.append("%i" % (breakpoint["id"]))
- if wait_for_resolve:
- self.wait_for_breakpoints_to_resolve(breakpoint_ids)
- return breakpoint_ids
def set_source_breakpoints_assembly(
self, source_reference, lines, data=None, wait_for_resolve=True
):
+ return self.set_source_breakpoints_from_source(
+ Source.build(source_reference=source_reference),
+ lines,
+ data,
+ wait_for_resolve,
+ )
+
+ def set_source_breakpoints_from_source(
+ self, source: Source, lines, data=None, wait_for_resolve=True
+ ):
response = self.dap_server.request_setBreakpoints(
- Source(source_reference=source_reference),
+ source,
lines,
data,
)
@@ -122,11 +123,19 @@ class DAPTestCaseBase(TestBase):
f"Expected to resolve all breakpoints. Unresolved breakpoint ids: {unresolved_breakpoints}",
)
- def waitUntil(self, condition_callback):
- for _ in range(20):
- if condition_callback():
+ def wait_until(
+ self,
+ predicate: Callable[[], bool],
+ delay: float = 0.5,
+ timeout: float = DEFAULT_TIMEOUT,
+ ) -> bool:
+ """Repeatedly run the predicate until either the predicate returns True
+ or a timeout has occurred."""
+ deadline = time.monotonic() + timeout
+ while deadline > time.monotonic():
+ if predicate():
return True
- time.sleep(0.5)
+ time.sleep(delay)
return False
def assertCapabilityIsSet(self, key: str, msg: Optional[str] = None) -> None:
@@ -139,13 +148,16 @@ class DAPTestCaseBase(TestBase):
if key in self.dap_server.capabilities:
self.assertEqual(self.dap_server.capabilities[key], False, msg)
- def verify_breakpoint_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+ def verify_breakpoint_hit(
+ self, breakpoint_ids: List[Union[int, str]], timeout: float = DEFAULT_TIMEOUT
+ ):
"""Wait for the process we are debugging to stop, and verify we hit
any breakpoint location in the "breakpoint_ids" array.
"breakpoint_ids" should be a list of breakpoint ID strings
(["1", "2"]). The return value from self.set_source_breakpoints()
or self.set_function_breakpoints() can be passed to this function"""
stopped_events = self.dap_server.wait_for_stopped(timeout)
+ normalized_bp_ids = [str(b) for b in breakpoint_ids]
for stopped_event in stopped_events:
if "body" in stopped_event:
body = stopped_event["body"]
@@ -156,22 +168,16 @@ class DAPTestCaseBase(TestBase):
and body["reason"] != "instruction breakpoint"
):
continue
- if "description" not in body:
+ if "hitBreakpointIds" not in body:
continue
- # Descriptions for breakpoints will be in the form
- # "breakpoint 1.1", so look for any description that matches
- # ("breakpoint 1.") in the description field as verification
- # that one of the breakpoint locations was hit. DAP doesn't
- # allow breakpoints to have multiple locations, but LLDB does.
- # So when looking at the description we just want to make sure
- # the right breakpoint matches and not worry about the actual
- # location.
- description = body["description"]
- for breakpoint_id in breakpoint_ids:
- match_desc = f"breakpoint {breakpoint_id}."
- if match_desc in description:
+ hit_breakpoint_ids = body["hitBreakpointIds"]
+ for bp in hit_breakpoint_ids:
+ if str(bp) in normalized_bp_ids:
return
- self.assertTrue(False, f"breakpoint not hit, stopped_events={stopped_events}")
+ self.assertTrue(
+ False,
+ f"breakpoint not hit, wanted breakpoint_ids {breakpoint_ids} in stopped_events {stopped_events}",
+ )
def verify_all_breakpoints_hit(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
"""Wait for the process we are debugging to stop, and verify we hit
@@ -215,7 +221,7 @@ class DAPTestCaseBase(TestBase):
return True
return False
- def verify_commands(self, flavor, output, commands):
+ def verify_commands(self, flavor: str, output: str, commands: list[str]):
self.assertTrue(output and len(output) > 0, "expect console output")
lines = output.splitlines()
prefix = "(lldb) "
@@ -228,10 +234,11 @@ class DAPTestCaseBase(TestBase):
found = True
break
self.assertTrue(
- found, "verify '%s' found in console output for '%s'" % (cmd, flavor)
+ found,
+ f"Command '{flavor}' - '{cmd}' not found in output: {output}",
)
- def get_dict_value(self, d, key_path):
+ def get_dict_value(self, d: dict, key_path: list[str]) -> Any:
"""Verify each key in the key_path array is in contained in each
dictionary within "d". Assert if any key isn't in the
corresponding dictionary. This is handy for grabbing values from VS
@@ -300,28 +307,34 @@ class DAPTestCaseBase(TestBase):
return (source["path"], stackFrame["line"])
return ("", 0)
- def get_stdout(self, timeout=0.0):
- return self.dap_server.get_output("stdout", timeout=timeout)
+ def get_stdout(self):
+ return self.dap_server.get_output("stdout")
- def get_console(self, timeout=0.0):
- return self.dap_server.get_output("console", timeout=timeout)
+ def get_console(self):
+ return self.dap_server.get_output("console")
- def get_important(self, timeout=0.0):
- return self.dap_server.get_output("important", timeout=timeout)
+ def get_important(self):
+ return self.dap_server.get_output("important")
- def collect_stdout(self, timeout_secs, pattern=None):
+ def collect_stdout(
+ self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
+ ) -> str:
return self.dap_server.collect_output(
- "stdout", timeout_secs=timeout_secs, pattern=pattern
+ "stdout", timeout=timeout, pattern=pattern
)
- def collect_console(self, timeout_secs, pattern=None):
+ def collect_console(
+ self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
+ ) -> str:
return self.dap_server.collect_output(
- "console", timeout_secs=timeout_secs, pattern=pattern
+ "console", timeout=timeout, pattern=pattern
)
- def collect_important(self, timeout_secs, pattern=None):
+ def collect_important(
+ self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
+ ) -> str:
return self.dap_server.collect_output(
- "important", timeout_secs=timeout_secs, pattern=pattern
+ "important", timeout=timeout, pattern=pattern
)
def get_local_as_int(self, name, threadId=None):
@@ -437,6 +450,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,
*,
@@ -464,9 +496,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,
@@ -495,10 +526,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/scripts/framework-header-fix.sh b/lldb/scripts/framework-header-fix.sh
deleted file mode 100755
index 345579c..0000000
--- a/lldb/scripts/framework-header-fix.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-# Usage: framework-header-fix.sh <source header dir> <LLDB Version>
-
-set -e
-
-for file in `find $1 -name "*.h"`
-do
- /usr/bin/sed -i.bak 's/\(#include\)[ ]*"lldb\/\(API\/\)\{0,1\}\(.*\)"/\1 <LLDB\/\3>/1' "$file"
- /usr/bin/sed -i.bak 's|<LLDB/Utility|<LLDB|' "$file"
- rm -f "$file.bak"
-done
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp
index 5b69cf1..b12cfce 100644
--- a/lldb/source/API/SBFrame.cpp
+++ b/lldb/source/API/SBFrame.cpp
@@ -97,226 +97,176 @@ bool SBFrame::IsValid() const {
}
SBFrame::operator bool() const {
LLDB_INSTRUMENT_VA(this);
-
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock()))
- return GetFrameSP().get() != nullptr;
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return false;
}
- // Without a target & process we can't have a valid stack frame.
- return false;
+ return GetFrameSP().get() != nullptr;
}
SBSymbolContext SBFrame::GetSymbolContext(uint32_t resolve_scope) const {
LLDB_INSTRUMENT_VA(this, resolve_scope);
SBSymbolContext sb_sym_ctx;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
- SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- if (StackFrame *frame = exe_ctx.GetFramePtr())
- sb_sym_ctx = frame->GetSymbolContext(scope);
- }
+
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return sb_sym_ctx;
}
+ SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope);
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ sb_sym_ctx = frame->GetSymbolContext(scope);
+
return sb_sym_ctx;
}
SBModule SBFrame::GetModule() const {
LLDB_INSTRUMENT_VA(this);
- SBModule sb_module;
- ModuleSP module_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp;
- sb_module.SetSP(module_sp);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBModule();
}
+ ModuleSP module_sp;
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (!frame)
+ return SBModule();
+
+ SBModule sb_module;
+ module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp;
+ sb_module.SetSP(module_sp);
return sb_module;
}
SBCompileUnit SBFrame::GetCompileUnit() const {
LLDB_INSTRUMENT_VA(this);
- SBCompileUnit sb_comp_unit;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- sb_comp_unit.reset(
- frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBCompileUnit();
}
- return sb_comp_unit;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBCompileUnit(
+ frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit);
+ return SBCompileUnit();
}
SBFunction SBFrame::GetFunction() const {
LLDB_INSTRUMENT_VA(this);
- SBFunction sb_function;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- sb_function.reset(
- frame->GetSymbolContext(eSymbolContextFunction).function);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBFunction();
}
- return sb_function;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBFunction(frame->GetSymbolContext(eSymbolContextFunction).function);
+ return SBFunction();
}
SBSymbol SBFrame::GetSymbol() const {
LLDB_INSTRUMENT_VA(this);
- SBSymbol sb_symbol;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- sb_symbol.reset(frame->GetSymbolContext(eSymbolContextSymbol).symbol);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBSymbol();
}
- return sb_symbol;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBSymbol(frame->GetSymbolContext(eSymbolContextSymbol).symbol);
+ return SBSymbol();
}
SBBlock SBFrame::GetBlock() const {
LLDB_INSTRUMENT_VA(this);
- SBBlock sb_block;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- sb_block.SetPtr(frame->GetSymbolContext(eSymbolContextBlock).block);
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBBlock();
}
- return sb_block;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBBlock(frame->GetSymbolContext(eSymbolContextBlock).block);
+ return SBBlock();
}
SBBlock SBFrame::GetFrameBlock() const {
LLDB_INSTRUMENT_VA(this);
- SBBlock sb_block;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- sb_block.SetPtr(frame->GetFrameBlock());
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBBlock();
}
- return sb_block;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBBlock(frame->GetFrameBlock());
+ return SBBlock();
}
SBLineEntry SBFrame::GetLineEntry() const {
LLDB_INSTRUMENT_VA(this);
- SBLineEntry sb_line_entry;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- sb_line_entry.SetLineEntry(
- frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBLineEntry();
}
- return sb_line_entry;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBLineEntry(
+ &frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
+ return SBLineEntry();
}
uint32_t SBFrame::GetFrameID() const {
LLDB_INSTRUMENT_VA(this);
- uint32_t frame_idx = UINT32_MAX;
-
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ constexpr uint32_t error_frame_idx = UINT32_MAX;
- StackFrame *frame = exe_ctx.GetFramePtr();
- if (frame)
- frame_idx = frame->GetFrameIndex();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return error_frame_idx;
+ }
- return frame_idx;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->GetFrameIndex();
+ return error_frame_idx;
}
lldb::addr_t SBFrame::GetCFA() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return LLDB_INVALID_ADDRESS;
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- if (frame)
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->GetStackID().GetCallFrameAddress();
return LLDB_INVALID_ADDRESS;
}
@@ -325,114 +275,86 @@ addr_t SBFrame::GetPC() const {
LLDB_INSTRUMENT_VA(this);
addr_t addr = LLDB_INVALID_ADDRESS;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- addr = frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
- target, AddressClass::eCode);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return addr;
}
+ Target *target = exe_ctx->GetTargetPtr();
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->GetFrameCodeAddress().GetOpcodeLoadAddress(
+ target, AddressClass::eCode);
+
return addr;
}
bool SBFrame::SetPC(addr_t new_pc) {
LLDB_INSTRUMENT_VA(this, new_pc);
- bool ret_val = false;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- if (StackFrame *frame = exe_ctx.GetFramePtr()) {
- if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) {
- ret_val = reg_ctx_sp->SetPC(new_pc);
- }
- }
- }
+ constexpr bool error_ret_val = false;
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return error_ret_val;
}
- return ret_val;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
+ return reg_ctx_sp->SetPC(new_pc);
+
+ return error_ret_val;
}
addr_t SBFrame::GetSP() const {
LLDB_INSTRUMENT_VA(this);
- addr_t addr = LLDB_INVALID_ADDRESS;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- if (StackFrame *frame = exe_ctx.GetFramePtr()) {
- if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) {
- addr = reg_ctx_sp->GetSP();
- }
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return LLDB_INVALID_ADDRESS;
}
- return addr;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
+ return reg_ctx_sp->GetSP();
+
+ return LLDB_INVALID_ADDRESS;
}
addr_t SBFrame::GetFP() const {
LLDB_INSTRUMENT_VA(this);
- addr_t addr = LLDB_INVALID_ADDRESS;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- if (StackFrame *frame = exe_ctx.GetFramePtr()) {
- if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) {
- addr = reg_ctx_sp->GetFP();
- }
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return LLDB_INVALID_ADDRESS;
}
- return addr;
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext())
+ return reg_ctx_sp->GetFP();
+
+ return LLDB_INVALID_ADDRESS;
}
SBAddress SBFrame::GetPCAddress() const {
LLDB_INSTRUMENT_VA(this);
- SBAddress sb_addr;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- sb_addr.SetAddress(frame->GetFrameCodeAddress());
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBAddress();
}
- return sb_addr;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return SBAddress(frame->GetFrameCodeAddress());
+ return SBAddress();
}
void SBFrame::Clear() {
@@ -445,12 +367,14 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) {
LLDB_INSTRUMENT_VA(this, var_path);
SBValue sb_value;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return sb_value;
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- if (frame && target) {
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
sb_value = GetValueForVariablePath(var_path, use_dynamic);
@@ -467,27 +391,22 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
return sb_value;
}
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- VariableSP var_sp;
- Status error;
- ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
- var_path, eNoDynamicValues,
- StackFrame::eExpressionPathOptionCheckPtrVsMember |
- StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
- var_sp, error));
- sb_value.SetSP(value_sp, use_dynamic);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return sb_value;
+ }
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
+ VariableSP var_sp;
+ Status error;
+ ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath(
+ var_path, eNoDynamicValues,
+ StackFrame::eExpressionPathOptionCheckPtrVsMember |
+ StackFrame::eExpressionPathOptionsAllowDirectIVarAccess,
+ var_sp, error));
+ sb_value.SetSP(value_sp, use_dynamic);
}
return sb_value;
}
@@ -495,18 +414,19 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path,
SBValue SBFrame::FindVariable(const char *name) {
LLDB_INSTRUMENT_VA(this, name);
- SBValue value;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValue();
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- if (frame && target) {
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
- value = FindVariable(name, use_dynamic);
+ return FindVariable(name, use_dynamic);
}
- return value;
+ return SBValue();
}
SBValue SBFrame::FindVariable(const char *name,
@@ -520,26 +440,17 @@ SBValue SBFrame::FindVariable(const char *name,
return sb_value;
}
- ValueObjectSP value_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- value_sp = frame->FindVariable(ConstString(name));
-
- if (value_sp)
- sb_value.SetSP(value_sp, use_dynamic);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return sb_value;
}
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ if (ValueObjectSP value_sp = frame->FindVariable(ConstString(name)))
+ sb_value.SetSP(value_sp, use_dynamic);
+
return sb_value;
}
@@ -547,12 +458,14 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type) {
LLDB_INSTRUMENT_VA(this, name, value_type);
SBValue value;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return value;
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- if (frame && target) {
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
value = FindValue(name, value_type, use_dynamic);
@@ -571,17 +484,17 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type,
}
ValueObjectSP value_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return value_sp;
+ } else {
+ Target *target = exe_ctx->GetTargetPtr();
+ Process *process = exe_ctx->GetProcessPtr();
+ if (target && process) { // FIXME: this check is redundant.
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
VariableList variable_list;
switch (value_type) {
@@ -698,10 +611,14 @@ bool SBFrame::operator!=(const SBFrame &rhs) const {
SBThread SBFrame::GetThread() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBThread();
+ }
- ThreadSP thread_sp(exe_ctx.GetThreadSP());
+ ThreadSP thread_sp(exe_ctx->GetThreadSP());
SBThread sb_thread(thread_sp);
return sb_thread;
@@ -710,19 +627,16 @@ SBThread SBFrame::GetThread() const {
const char *SBFrame::Disassemble() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (!target || !process)
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
-
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- if (auto *frame = exe_ctx.GetFramePtr())
- return ConstString(frame->Disassemble()).GetCString();
}
+ if (auto *frame = exe_ctx->GetFramePtr())
+ return ConstString(frame->Disassemble()).GetCString();
+
return nullptr;
}
@@ -731,12 +645,15 @@ SBValueList SBFrame::GetVariables(bool arguments, bool locals, bool statics,
LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only);
SBValueList value_list;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return value_list;
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- if (frame && target) {
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
+ Target *target = exe_ctx->GetTargetPtr();
lldb::DynamicValueType use_dynamic =
frame->CalculateTarget()->GetPreferDynamicValue();
const bool include_runtime_support_values =
@@ -761,12 +678,16 @@ lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals,
LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only,
use_dynamic);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValueList();
+ }
- Target *target = exe_ctx.GetTargetPtr();
+ Target *target = exe_ctx->GetTargetPtr();
const bool include_runtime_support_values =
- target ? target->GetDisplayRuntimeSupportValues() : false;
+ target->GetDisplayRuntimeSupportValues();
SBVariablesOptions options;
options.SetIncludeArguments(arguments);
options.SetIncludeLocals(locals);
@@ -781,30 +702,26 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) {
LLDB_INSTRUMENT_VA(this, options);
SBValueList value_list;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
-
- const bool statics = options.GetIncludeStatics();
- const bool arguments = options.GetIncludeArguments();
- const bool recognized_arguments =
- options.GetIncludeRecognizedArguments(SBTarget(exe_ctx.GetTargetSP()));
- const bool locals = options.GetIncludeLocals();
- const bool in_scope_only = options.GetInScopeOnly();
- const bool include_runtime_support_values =
- options.GetIncludeRuntimeSupportValues();
- const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
-
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValueList();
+ } else {
+ const bool statics = options.GetIncludeStatics();
+ const bool arguments = options.GetIncludeArguments();
+ const bool recognized_arguments =
+ options.GetIncludeRecognizedArguments(SBTarget(exe_ctx->GetTargetSP()));
+ const bool locals = options.GetIncludeLocals();
+ const bool in_scope_only = options.GetInScopeOnly();
+ const bool include_runtime_support_values =
+ options.GetIncludeRuntimeSupportValues();
+ const lldb::DynamicValueType use_dynamic = options.GetUseDynamic();
- std::set<VariableSP> variable_set;
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
+ std::set<VariableSP> variable_set;
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process) { // FIXME: this check is redundant.
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
Debugger &dbg = process->GetTarget().GetDebugger();
VariableList *variable_list = nullptr;
Status var_error;
@@ -892,17 +809,16 @@ SBValueList SBFrame::GetRegisters() {
LLDB_INSTRUMENT_VA(this);
SBValueList value_list;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValueList();
+ } else {
+ Target *target = exe_ctx->GetTargetPtr();
+ Process *process = exe_ctx->GetProcessPtr();
+ if (target && process) { // FIXME: this check is redundant.
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
RegisterContextSP reg_ctx(frame->GetRegisterContext());
if (reg_ctx) {
const uint32_t num_sets = reg_ctx->GetRegisterSetCount();
@@ -923,17 +839,16 @@ SBValue SBFrame::FindRegister(const char *name) {
SBValue result;
ValueObjectSP value_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValue();
+ } else {
+ Target *target = exe_ctx->GetTargetPtr();
+ Process *process = exe_ctx->GetProcessPtr();
+ if (target && process) { // FIXME: this check is redundant.
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
RegisterContextSP reg_ctx(frame->GetRegisterContext());
if (reg_ctx) {
if (const RegisterInfo *reg_info =
@@ -953,12 +868,11 @@ SBError SBFrame::GetDescriptionWithFormat(const SBFormat &format,
SBStream &output) {
Stream &strm = output.ref();
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
SBError error;
if (!format) {
@@ -966,16 +880,9 @@ SBError SBFrame::GetDescriptionWithFormat(const SBFormat &format,
return error;
}
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame &&
- frame->DumpUsingFormat(strm, format.GetFormatEntrySP().get())) {
- return error;
- }
- }
- }
+ if (StackFrame *frame = exe_ctx->GetFramePtr();
+ frame && frame->DumpUsingFormat(strm, format.GetFormatEntrySP().get()))
+ return error;
error.SetErrorStringWithFormat(
"It was not possible to generate a frame "
"description with the given format string '%s'",
@@ -988,23 +895,16 @@ bool SBFrame::GetDescription(SBStream &description) {
Stream &strm = description.ref();
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- frame->DumpUsingSettingsFormat(&strm);
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ strm.PutCString("Error: process is not stopped.");
+ return true;
+ }
- } else
- strm.PutCString("No value");
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ frame->DumpUsingSettingsFormat(&strm);
return true;
}
@@ -1012,22 +912,24 @@ bool SBFrame::GetDescription(SBStream &description) {
SBValue SBFrame::EvaluateExpression(const char *expr) {
LLDB_INSTRUMENT_VA(this, expr);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return CreateProcessIsRunningExprEvalError();
+ }
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
SBExpressionOptions options;
- if (frame && target) {
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (frame) {
lldb::DynamicValueType fetch_dynamic_value =
frame->CalculateTarget()->GetPreferDynamicValue();
options.SetFetchDynamicValue(fetch_dynamic_value);
}
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
- SourceLanguage language;
- if (target)
- language = target->GetLanguage();
+ Target *target = exe_ctx->GetTargetPtr();
+ SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
@@ -1043,14 +945,16 @@ SBFrame::EvaluateExpression(const char *expr,
options.SetFetchDynamicValue(fetch_dynamic_value);
options.SetUnwindOnError(true);
options.SetIgnoreBreakpoints(true);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- SourceLanguage language;
- if (target)
- language = target->GetLanguage();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return CreateProcessIsRunningExprEvalError();
+ }
+
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ Target *target = exe_ctx->GetTargetPtr();
+ SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
@@ -1063,23 +967,35 @@ SBValue SBFrame::EvaluateExpression(const char *expr,
LLDB_INSTRUMENT_VA(this, expr, fetch_dynamic_value, unwind_on_error);
SBExpressionOptions options;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return CreateProcessIsRunningExprEvalError();
+ }
options.SetFetchDynamicValue(fetch_dynamic_value);
options.SetUnwindOnError(unwind_on_error);
options.SetIgnoreBreakpoints(true);
- StackFrame *frame = exe_ctx.GetFramePtr();
- Target *target = exe_ctx.GetTargetPtr();
- SourceLanguage language;
- if (target)
- language = target->GetLanguage();
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ Target *target = exe_ctx->GetTargetPtr();
+ SourceLanguage language = target->GetLanguage();
if (!language && frame)
language = frame->GetLanguage();
options.SetLanguage((SBSourceLanguageName)language.name, language.version);
return EvaluateExpression(expr, options);
}
+lldb::SBValue SBFrame::CreateProcessIsRunningExprEvalError() {
+ auto error = Status::FromErrorString("can't evaluate expressions when the "
+ "process is running.");
+ ValueObjectSP expr_value_sp =
+ ValueObjectConstResult::Create(nullptr, std::move(error));
+ SBValue expr_result;
+ expr_result.SetSP(expr_value_sp, false);
+ return expr_result;
+}
+
lldb::SBValue SBFrame::EvaluateExpression(const char *expr,
const SBExpressionOptions &options) {
LLDB_INSTRUMENT_VA(this, expr, options);
@@ -1094,18 +1010,16 @@ lldb::SBValue SBFrame::EvaluateExpression(const char *expr,
ValueObjectSP expr_value_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
-
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ expr_result = CreateProcessIsRunningExprEvalError();
+ } else {
+ Target *target = exe_ctx->GetTargetPtr();
+ Process *process = exe_ctx->GetProcessPtr();
+ if (target && process) { // FIXME: this check is redundant.
+ if (StackFrame *frame = exe_ctx->GetFramePtr()) {
std::unique_ptr<llvm::PrettyStackTraceFormat> stack_trace;
if (target->GetDisplayExpressionsInCrashlogs()) {
StreamString frame_description;
@@ -1122,16 +1036,10 @@ lldb::SBValue SBFrame::EvaluateExpression(const char *expr,
}
} else {
Status error;
- error = Status::FromErrorString("can't evaluate expressions when the "
- "process is running.");
- expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error));
- expr_result.SetSP(expr_value_sp, false);
- }
- } else {
- Status error;
error = Status::FromErrorString("sbframe object is not valid.");
expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error));
expr_result.SetSP(expr_value_sp, false);
+ }
}
if (expr_result.GetError().Success())
@@ -1152,9 +1060,13 @@ SBStructuredData SBFrame::GetLanguageSpecificData() const {
LLDB_INSTRUMENT_VA(this);
SBStructuredData sb_data;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
- StackFrame *frame = exe_ctx.GetFramePtr();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return sb_data;
+ }
+ StackFrame *frame = exe_ctx->GetFramePtr();
if (!frame)
return sb_data;
@@ -1172,20 +1084,15 @@ bool SBFrame::IsInlined() {
bool SBFrame::IsInlined() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- return frame->IsInlined();
- }
+ 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->IsInlined();
return false;
}
@@ -1198,10 +1105,14 @@ bool SBFrame::IsArtificial() {
bool SBFrame::IsArtificial() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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())
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsArtificial();
return false;
@@ -1210,10 +1121,14 @@ bool SBFrame::IsArtificial() const {
bool SBFrame::IsHidden() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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())
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
return frame->IsHidden();
return false;
@@ -1228,63 +1143,44 @@ const char *SBFrame::GetFunctionName() {
lldb::LanguageType SBFrame::GuessLanguage() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame) {
- return frame->GuessLanguage().AsLanguageType();
- }
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return eLanguageTypeUnknown;
}
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->GuessLanguage().AsLanguageType();
return eLanguageTypeUnknown;
}
const char *SBFrame::GetFunctionName() const {
LLDB_INSTRUMENT_VA(this);
- const char *name = nullptr;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- return frame->GetFunctionName();
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return nullptr;
}
- return name;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->GetFunctionName();
+ return nullptr;
}
const char *SBFrame::GetDisplayFunctionName() {
LLDB_INSTRUMENT_VA(this);
- const char *name = nullptr;
-
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- StackFrame *frame = nullptr;
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock())) {
- frame = exe_ctx.GetFramePtr();
- if (frame)
- return frame->GetDisplayFunctionName();
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return nullptr;
}
- return name;
+
+ if (StackFrame *frame = exe_ctx->GetFramePtr())
+ return frame->GetDisplayFunctionName();
+ return nullptr;
}
diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp
index 19861f6..65b02d6 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 a77a703..cd9b857 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/SBModule.cpp b/lldb/source/API/SBModule.cpp
index 985107e..5a57f45 100644
--- a/lldb/source/API/SBModule.cpp
+++ b/lldb/source/API/SBModule.cpp
@@ -671,3 +671,11 @@ void SBModule::GarbageCollectAllocatedModules() {
const bool mandatory = false;
ModuleList::RemoveOrphanSharedModules(mandatory);
}
+
+const char *SBModule::GetObjectName() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (!m_opaque_sp)
+ return nullptr;
+ return m_opaque_sp->GetObjectName().AsCString();
+}
diff --git a/lldb/source/API/SBStructuredData.cpp b/lldb/source/API/SBStructuredData.cpp
index b891a34..8e2c18e 100644
--- a/lldb/source/API/SBStructuredData.cpp
+++ b/lldb/source/API/SBStructuredData.cpp
@@ -232,3 +232,47 @@ lldb::SBScriptObject SBStructuredData::GetGenericValue() const {
return {m_impl_up->GetGenericValue(), eScriptLanguageDefault};
}
+
+void SBStructuredData::SetValueForKey(const char *key,
+ SBStructuredData &value) {
+ LLDB_INSTRUMENT_VA(this, key, value);
+
+ if (StructuredData::ObjectSP obj_sp = value.m_impl_up->GetObjectSP())
+ m_impl_up->SetValueForKey(key, obj_sp);
+}
+
+void SBStructuredData::SetUnsignedIntegerValue(uint64_t value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetUnsignedIntegerValue(value);
+}
+
+void SBStructuredData::SetSignedIntegerValue(int64_t value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetSignedIntegerValue(value);
+}
+
+void SBStructuredData::SetFloatValue(double value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetFloatValue(value);
+}
+
+void SBStructuredData::SetBooleanValue(bool value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetBooleanValue(value);
+}
+
+void SBStructuredData::SetStringValue(const char *value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetStringValue(value);
+}
+
+void SBStructuredData::SetGenericValue(SBScriptObject value) {
+ LLDB_INSTRUMENT_VA(this, value);
+
+ m_impl_up->SetGenericValue(value.GetPointer());
+}
diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp
index 79477dd..3030c83 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);
@@ -193,6 +202,14 @@ SymbolType SBSymbol::GetType() {
return eSymbolTypeInvalid;
}
+uint32_t SBSymbol::GetID() {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->GetID();
+ return 0;
+}
+
bool SBSymbol::IsExternal() {
LLDB_INSTRUMENT_VA(this);
@@ -208,3 +225,23 @@ bool SBSymbol::IsSynthetic() {
return m_opaque_ptr->IsSynthetic();
return false;
}
+
+bool SBSymbol::IsDebug() {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (m_opaque_ptr)
+ return m_opaque_ptr->IsDebug();
+ return false;
+}
+
+const char *SBSymbol::GetTypeAsString(lldb::SymbolType symbol_type) {
+ LLDB_INSTRUMENT_VA(symbol_type);
+
+ return Symbol::GetTypeAsString(symbol_type);
+}
+
+lldb::SymbolType SBSymbol::GetTypeFromString(const char *str) {
+ LLDB_INSTRUMENT_VA(str);
+
+ return Symbol::GetTypeFromString(str);
+}
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index f26f795..eb56337 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -766,16 +766,19 @@ SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name,
const bool hardware = false;
const LazyBool skip_prologue = eLazyBoolCalculate;
const lldb::addr_t offset = 0;
+ const bool offset_is_insn_count = false;
if (module_name && module_name[0]) {
FileSpecList module_spec_list;
module_spec_list.Append(FileSpec(module_name));
sb_bp = target_sp->CreateBreakpoint(
&module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto,
- eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
+ eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
+ internal, hardware);
} else {
sb_bp = target_sp->CreateBreakpoint(
nullptr, nullptr, symbol_name, eFunctionNameTypeAuto,
- eLanguageTypeUnknown, offset, skip_prologue, internal, hardware);
+ eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue,
+ internal, hardware);
}
}
@@ -811,6 +814,17 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
const SBFileSpecList &comp_unit_list) {
LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language,
module_list, comp_unit_list);
+ return BreakpointCreateByName(symbol_name, name_type_mask, symbol_language, 0,
+ false, module_list, comp_unit_list);
+}
+
+lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
+ const char *symbol_name, uint32_t name_type_mask,
+ LanguageType symbol_language, lldb::addr_t offset,
+ bool offset_is_insn_count, const SBFileSpecList &module_list,
+ const SBFileSpecList &comp_unit_list) {
+ LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language, offset,
+ offset_is_insn_count, module_list, comp_unit_list);
SBBreakpoint sb_bp;
if (TargetSP target_sp = GetSP();
@@ -821,7 +835,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName(
std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask);
sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(),
- symbol_name, mask, symbol_language, 0,
+ symbol_name, mask, symbol_language,
+ offset, offset_is_insn_count,
skip_prologue, internal, hardware);
}
@@ -1555,6 +1570,18 @@ SBModule SBTarget::FindModule(const SBFileSpec &sb_file_spec) {
return sb_module;
}
+SBModule SBTarget::FindModule(const SBModuleSpec &sb_module_spec) {
+ LLDB_INSTRUMENT_VA(this, sb_module_spec);
+
+ SBModule sb_module;
+ if (TargetSP target_sp = GetSP(); target_sp && sb_module_spec.IsValid()) {
+ // The module list is thread safe, no need to lock.
+ sb_module.SetSP(
+ target_sp->GetImages().FindFirstModule(*sb_module_spec.m_opaque_up));
+ }
+ return sb_module;
+}
+
SBSymbolContextList SBTarget::FindCompileUnits(const SBFileSpec &sb_file_spec) {
LLDB_INSTRUMENT_VA(this, sb_file_spec);
@@ -1955,29 +1982,10 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr,
if (TargetSP target_sp = GetSP()) {
if (Address *addr_ptr = base_addr.get()) {
- DataBufferHeap data(
- target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0);
- bool force_live_memory = true;
- lldb_private::Status error;
- lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
- const size_t bytes_read =
- target_sp->ReadMemory(*addr_ptr, data.GetBytes(), data.GetByteSize(),
- error, force_live_memory, &load_addr);
-
- const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
- if (!flavor_string || flavor_string[0] == '\0') {
- // FIXME - we don't have the mechanism in place to do per-architecture
- // settings. But since we know that for now we only support flavors on
- // x86 & x86_64,
- const llvm::Triple::ArchType arch =
- target_sp->GetArchitecture().GetTriple().getArch();
- if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64)
- flavor_string = target_sp->GetDisassemblyFlavor();
+ if (llvm::Expected<DisassemblerSP> disassembler =
+ target_sp->ReadInstructions(*addr_ptr, count, flavor_string)) {
+ sb_instructions.SetDisassembler(*disassembler);
}
- sb_instructions.SetDisassembler(Disassembler::DisassembleBytes(
- target_sp->GetArchitecture(), nullptr, flavor_string,
- target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
- *addr_ptr, data.GetBytes(), bytes_read, count, data_from_file));
}
}
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 74bc66c..ec68b2a 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -90,16 +90,17 @@ lldb::SBQueue SBThread::GetQueue() const {
SBQueue sb_queue;
QueueSP queue_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- queue_sp = exe_ctx.GetThreadPtr()->GetQueue();
- if (queue_sp) {
- sb_queue.SetQueue(queue_sp);
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBQueue();
+ }
+
+ if (exe_ctx->HasThreadScope()) {
+ queue_sp = exe_ctx->GetThreadPtr()->GetQueue();
+ if (queue_sp) {
+ sb_queue.SetQueue(queue_sp);
}
}
@@ -112,19 +113,17 @@ bool SBThread::IsValid() const {
}
SBThread::operator bool() const {
LLDB_INSTRUMENT_VA(this);
+ if (!m_opaque_sp)
+ return false;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- Target *target = exe_ctx.GetTargetPtr();
- Process *process = exe_ctx.GetProcessPtr();
- if (target && process) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&process->GetRunLock()))
- return m_opaque_sp->GetThreadSP().get() != nullptr;
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return false;
}
- // Without a valid target & process, this thread can't be valid.
- return false;
+
+ return m_opaque_sp->GetThreadSP().get() != nullptr;
}
void SBThread::Clear() {
@@ -137,29 +136,27 @@ StopReason SBThread::GetStopReason() {
LLDB_INSTRUMENT_VA(this);
StopReason reason = eStopReasonInvalid;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- return exe_ctx.GetThreadPtr()->GetStopReason();
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return reason;
}
+ if (exe_ctx->HasThreadScope())
+ return exe_ctx->GetThreadPtr()->GetStopReason();
+
return reason;
}
size_t SBThread::GetStopReasonDataCount() {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ 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) {
@@ -179,7 +176,7 @@ size_t SBThread::GetStopReasonDataCount() {
case eStopReasonBreakpoint: {
break_id_t site_id = stop_info_sp->GetValue();
lldb::BreakpointSiteSP bp_site_sp(
- exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID(
+ exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID(
site_id));
if (bp_site_sp)
return bp_site_sp->GetNumberOfConstituents() * 2;
@@ -207,6 +204,9 @@ size_t SBThread::GetStopReasonDataCount() {
}
}
}
+ } else {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return 0;
}
return 0;
}
@@ -214,13 +214,11 @@ size_t SBThread::GetStopReasonDataCount() {
uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
LLDB_INSTRUMENT_VA(this, idx);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- Thread *thread = exe_ctx.GetThreadPtr();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (exe_ctx) {
+ 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();
@@ -241,7 +239,7 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
case eStopReasonBreakpoint: {
break_id_t site_id = stop_info_sp->GetValue();
lldb::BreakpointSiteSP bp_site_sp(
- exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID(
+ exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID(
site_id));
if (bp_site_sp) {
uint32_t bp_index = idx / 2;
@@ -280,6 +278,9 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
}
}
}
+ } else {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return 0;
}
return 0;
}
@@ -289,13 +290,17 @@ bool SBThread::GetStopReasonExtendedInfoAsJSON(lldb::SBStream &stream) {
Stream &strm = stream.ref();
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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 (!exe_ctx.HasThreadScope())
+ if (!exe_ctx->HasThreadScope())
return false;
- StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo();
+ StopInfoSP stop_info = exe_ctx->GetThreadPtr()->GetStopInfo();
StructuredData::ObjectSP info = stop_info->GetExtendedInfo();
if (!info)
return false;
@@ -311,15 +316,19 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) {
SBThreadCollection threads;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBThreadCollection();
+ }
- if (!exe_ctx.HasThreadScope())
+ if (!exe_ctx->HasThreadScope())
return SBThreadCollection();
- ProcessSP process_sp = exe_ctx.GetProcessSP();
+ ProcessSP process_sp = exe_ctx->GetProcessSP();
- StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo();
+ StopInfoSP stop_info = exe_ctx->GetThreadPtr()->GetStopInfo();
StructuredData::ObjectSP info = stop_info->GetExtendedInfo();
if (!info)
return threads;
@@ -332,20 +341,20 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) {
size_t SBThread::GetStopDescription(char *dst, size_t dst_len) {
LLDB_INSTRUMENT_VA(this, dst, dst_len);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
if (dst)
*dst = 0;
- if (!exe_ctx.HasThreadScope())
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return 0;
+ }
- Process::StopLocker stop_locker;
- if (!stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock()))
+ if (!exe_ctx->HasThreadScope())
return 0;
- std::string thread_stop_desc = exe_ctx.GetThreadPtr()->GetStopDescription();
+ std::string thread_stop_desc = exe_ctx->GetThreadPtr()->GetStopDescription();
if (thread_stop_desc.empty())
return 0;
@@ -361,16 +370,17 @@ SBValue SBThread::GetStopReturnValue() {
LLDB_INSTRUMENT_VA(this);
ValueObjectSP return_valobj_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBValue();
+ }
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo();
- if (stop_info_sp) {
- return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp);
- }
+ if (exe_ctx->HasThreadScope()) {
+ StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo();
+ if (stop_info_sp) {
+ return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp);
}
}
@@ -402,47 +412,48 @@ uint32_t SBThread::GetIndexID() const {
const char *SBThread::GetName() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (!exe_ctx.HasThreadScope())
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
+ }
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock()))
- return ConstString(exe_ctx.GetThreadPtr()->GetName()).GetCString();
+ if (!exe_ctx->HasThreadScope())
+ return nullptr;
- return nullptr;
+ return ConstString(exe_ctx->GetThreadPtr()->GetName()).GetCString();
}
const char *SBThread::GetQueueName() const {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (!exe_ctx.HasThreadScope())
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
return nullptr;
+ }
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock()))
- return ConstString(exe_ctx.GetThreadPtr()->GetQueueName()).GetCString();
+ if (!exe_ctx->HasThreadScope())
+ return nullptr;
- return nullptr;
+ return ConstString(exe_ctx->GetThreadPtr()->GetQueueName()).GetCString();
}
lldb::queue_id_t SBThread::GetQueueID() const {
LLDB_INSTRUMENT_VA(this);
queue_id_t id = LLDB_INVALID_QUEUE_ID;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return id;
+ }
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- id = exe_ctx.GetThreadPtr()->GetQueueID();
- }
+ if (exe_ctx->HasThreadScope()) {
+ id = exe_ctx->GetThreadPtr()->GetQueueID();
}
return id;
@@ -452,13 +463,11 @@ bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) {
LLDB_INSTRUMENT_VA(this, path, strm);
bool success = false;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- Thread *thread = exe_ctx.GetThreadPtr();
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (exe_ctx) {
+ if (exe_ctx->HasThreadScope()) {
+ Thread *thread = exe_ctx->GetThreadPtr();
StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo();
if (info_root_sp) {
StructuredData::ObjectSP node =
@@ -490,16 +499,16 @@ bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) {
}
}
}
+ } else {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return success;
}
return success;
}
-static Status ResumeNewPlan(ExecutionContext &exe_ctx, ThreadPlan *new_plan) {
- Process *process = exe_ctx.GetProcessPtr();
- if (!process)
- return Status::FromErrorString("No process in SBThread::ResumeNewPlan");
-
+static Status ResumeNewPlan(StoppedExecutionContext exe_ctx,
+ ThreadPlan *new_plan) {
Thread *thread = exe_ctx.GetThreadPtr();
if (!thread)
return Status::FromErrorString("No thread in SBThread::ResumeNewPlan");
@@ -512,8 +521,11 @@ static Status ResumeNewPlan(ExecutionContext &exe_ctx, ThreadPlan *new_plan) {
}
// Why do we need to set the current thread by ID here???
+ Process *process = exe_ctx.GetProcessPtr();
process->GetThreadList().SetSelectedThreadByID(thread->GetID());
+ // Release the run lock but keep the API lock.
+ std::unique_lock<std::recursive_mutex> api_lock = exe_ctx.AllowResume();
if (process->GetTarget().GetDebugger().GetAsyncExecution())
return process->Resume();
return process->ResumeSynchronous(nullptr);
@@ -529,15 +541,19 @@ void SBThread::StepOver(lldb::RunMode stop_other_threads) {
void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) {
LLDB_INSTRUMENT_VA(this, stop_other_threads, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
bool abort_other_plans = false;
StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0));
@@ -555,7 +571,7 @@ void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) {
true, abort_other_plans, stop_other_threads, new_plan_status);
}
}
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
}
void SBThread::StepInto(lldb::RunMode stop_other_threads) {
@@ -576,17 +592,21 @@ void SBThread::StepInto(const char *target_name, uint32_t end_line,
SBError &error, lldb::RunMode stop_other_threads) {
LLDB_INSTRUMENT_VA(this, target_name, end_line, error, stop_other_threads);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
bool abort_other_plans = false;
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0));
ThreadPlanSP new_plan_sp;
Status new_plan_status;
@@ -618,7 +638,7 @@ void SBThread::StepInto(const char *target_name, uint32_t end_line,
}
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -633,10 +653,14 @@ void SBThread::StepOut() {
void SBThread::StepOut(SBError &error) {
LLDB_INSTRUMENT_VA(this, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
@@ -644,7 +668,7 @@ void SBThread::StepOut(SBError &error) {
bool abort_other_plans = false;
bool stop_other_threads = false;
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
const LazyBool avoid_no_debug = eLazyBoolCalculate;
Status new_plan_status;
@@ -653,7 +677,7 @@ void SBThread::StepOut(SBError &error) {
eVoteNoOpinion, 0, new_plan_status, avoid_no_debug));
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -668,8 +692,12 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame) {
void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) {
LLDB_INSTRUMENT_VA(this, sb_frame, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
if (!sb_frame.IsValid()) {
error = Status::FromErrorString("passed invalid SBFrame object");
@@ -678,14 +706,14 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) {
StackFrameSP frame_sp(sb_frame.GetFrameSP());
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
bool abort_other_plans = false;
bool stop_other_threads = false;
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
if (sb_frame.GetThread().GetThreadID() != thread->GetID()) {
error = Status::FromErrorString("passed a frame from another thread");
return;
@@ -697,7 +725,7 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) {
eVoteNoOpinion, frame_sp->GetFrameIndex(), new_plan_status));
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -712,21 +740,25 @@ void SBThread::StepInstruction(bool step_over) {
void SBThread::StepInstruction(bool step_over, SBError &error) {
LLDB_INSTRUMENT_VA(this, step_over, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
Status new_plan_status;
ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction(
step_over, false, true, new_plan_status));
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -741,10 +773,14 @@ void SBThread::RunToAddress(lldb::addr_t addr) {
void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) {
LLDB_INSTRUMENT_VA(this, addr, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return;
+ }
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return;
}
@@ -754,14 +790,14 @@ void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) {
Address target_addr(addr);
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
Status new_plan_status;
ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress(
abort_other_plans, target_addr, stop_other_threads, new_plan_status));
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -773,14 +809,16 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
SBError sb_error;
char path[PATH_MAX];
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
StackFrameSP frame_sp(sb_frame.GetFrameSP());
- if (exe_ctx.HasThreadScope()) {
- Target *target = exe_ctx.GetTargetPtr();
- Thread *thread = exe_ctx.GetThreadPtr();
+ if (exe_ctx->HasThreadScope()) {
+ Target *target = exe_ctx->GetTargetPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
if (line == 0) {
sb_error = Status::FromErrorString("invalid line argument");
@@ -875,7 +913,7 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame,
frame_sp->GetFrameIndex(), new_plan_status));
if (new_plan_status.Success())
- sb_error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ sb_error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
sb_error = Status::FromErrorString(new_plan_status.AsCString());
}
@@ -907,15 +945,17 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name,
SBError error;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
error = Status::FromErrorString("this SBThread object is invalid");
return error;
}
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
Status new_plan_status;
StructuredData::ObjectSP obj_sp = args_data.m_impl_up->GetObjectSP();
@@ -931,7 +971,7 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name,
return error;
if (new_plan_status.Success())
- error = ResumeNewPlan(exe_ctx, new_plan_sp.get());
+ error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
else
error = Status::FromErrorString(new_plan_status.AsCString());
@@ -943,15 +983,17 @@ SBError SBThread::JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line) {
SBError sb_error;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
- if (!exe_ctx.HasThreadScope()) {
+ if (!exe_ctx->HasThreadScope()) {
sb_error = Status::FromErrorString("this SBThread object is invalid");
return sb_error;
}
- Thread *thread = exe_ctx.GetThreadPtr();
+ Thread *thread = exe_ctx->GetThreadPtr();
Status err = thread->JumpToLine(file_spec.ref(), line, true);
sb_error.SetError(std::move(err));
@@ -963,11 +1005,13 @@ SBError SBThread::ReturnFromFrame(SBFrame &frame, SBValue &return_value) {
SBError sb_error;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
- if (exe_ctx.HasThreadScope()) {
- Thread *thread = exe_ctx.GetThreadPtr();
+ if (exe_ctx->HasThreadScope()) {
+ Thread *thread = exe_ctx->GetThreadPtr();
sb_error.SetError(
thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP()));
}
@@ -980,11 +1024,13 @@ SBError SBThread::UnwindInnermostExpression() {
SBError sb_error;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx)
+ return Status::FromError(exe_ctx.takeError());
- if (exe_ctx.HasThreadScope()) {
- Thread *thread = exe_ctx.GetThreadPtr();
+ if (exe_ctx->HasThreadScope()) {
+ Thread *thread = exe_ctx->GetThreadPtr();
sb_error.SetError(thread->UnwindInnermostExpression());
if (sb_error.Success())
thread->SetSelectedFrameByIndex(0, false);
@@ -1003,18 +1049,17 @@ bool SBThread::Suspend() {
bool SBThread::Suspend(SBError &error) {
LLDB_INSTRUMENT_VA(this, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ error = Status::FromError(exe_ctx.takeError());
+ return false;
+ }
bool result = false;
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- exe_ctx.GetThreadPtr()->SetResumeState(eStateSuspended);
- result = true;
- } else {
- error = Status::FromErrorString("process is running");
- }
+ if (exe_ctx->HasThreadScope()) {
+ exe_ctx->GetThreadPtr()->SetResumeState(eStateSuspended);
+ result = true;
} else
error = Status::FromErrorString("this SBThread object is invalid");
return result;
@@ -1030,19 +1075,19 @@ bool SBThread::Resume() {
bool SBThread::Resume(SBError &error) {
LLDB_INSTRUMENT_VA(this, error);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ error = Status::FromErrorString("process is running");
+ return false;
+ }
bool result = false;
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- const bool override_suspend = true;
- exe_ctx.GetThreadPtr()->SetResumeState(eStateRunning, override_suspend);
- result = true;
- } else {
- error = Status::FromErrorString("process is running");
- }
+ if (exe_ctx->HasThreadScope()) {
+ const bool override_suspend = true;
+ exe_ctx->GetThreadPtr()->SetResumeState(eStateRunning, override_suspend);
+ result = true;
} else
error = Status::FromErrorString("this SBThread object is invalid");
return result;
@@ -1051,22 +1096,30 @@ bool SBThread::Resume(SBError &error) {
bool SBThread::IsSuspended() {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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 (exe_ctx.HasThreadScope())
- return exe_ctx.GetThreadPtr()->GetResumeState() == eStateSuspended;
+ if (exe_ctx->HasThreadScope())
+ return exe_ctx->GetThreadPtr()->GetResumeState() == eStateSuspended;
return false;
}
bool SBThread::IsStopped() {
LLDB_INSTRUMENT_VA(this);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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 (exe_ctx.HasThreadScope())
- return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true);
+ if (exe_ctx->HasThreadScope())
+ return StateIsStoppedState(exe_ctx->GetThreadPtr()->GetState(), true);
return false;
}
@@ -1074,13 +1127,17 @@ SBProcess SBThread::GetProcess() {
LLDB_INSTRUMENT_VA(this);
SBProcess sb_process;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBProcess();
+ }
- if (exe_ctx.HasThreadScope()) {
+ if (exe_ctx->HasThreadScope()) {
// Have to go up to the target so we can get a shared pointer to our
// process...
- sb_process.SetSP(exe_ctx.GetProcessSP());
+ sb_process.SetSP(exe_ctx->GetProcessSP());
}
return sb_process;
@@ -1089,34 +1146,33 @@ SBProcess SBThread::GetProcess() {
uint32_t SBThread::GetNumFrames() {
LLDB_INSTRUMENT_VA(this);
- uint32_t num_frames = 0;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount();
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return 0;
}
- return num_frames;
+ if (exe_ctx->HasThreadScope())
+ return exe_ctx->GetThreadPtr()->GetStackFrameCount();
+
+ return 0;
}
SBFrame SBThread::GetFrameAtIndex(uint32_t idx) {
LLDB_INSTRUMENT_VA(this, idx);
SBFrame sb_frame;
- StackFrameSP frame_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBFrame();
+ }
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(idx);
- sb_frame.SetFrameSP(frame_sp);
- }
+ if (exe_ctx->HasThreadScope()) {
+ StackFrameSP frame_sp = exe_ctx->GetThreadPtr()->GetStackFrameAtIndex(idx);
+ sb_frame.SetFrameSP(frame_sp);
}
return sb_frame;
@@ -1126,17 +1182,17 @@ lldb::SBFrame SBThread::GetSelectedFrame() {
LLDB_INSTRUMENT_VA(this);
SBFrame sb_frame;
- StackFrameSP frame_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- frame_sp =
- exe_ctx.GetThreadPtr()->GetSelectedFrame(SelectMostRelevantFrame);
- sb_frame.SetFrameSP(frame_sp);
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBFrame();
+ }
+
+ if (exe_ctx->HasThreadScope()) {
+ StackFrameSP frame_sp =
+ exe_ctx->GetThreadPtr()->GetSelectedFrame(SelectMostRelevantFrame);
+ sb_frame.SetFrameSP(frame_sp);
}
return sb_frame;
@@ -1147,18 +1203,19 @@ lldb::SBFrame SBThread::SetSelectedFrame(uint32_t idx) {
SBFrame sb_frame;
StackFrameSP frame_sp;
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
-
- if (exe_ctx.HasThreadScope()) {
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- Thread *thread = exe_ctx.GetThreadPtr();
- frame_sp = thread->GetStackFrameAtIndex(idx);
- if (frame_sp) {
- thread->SetSelectedFrame(frame_sp.get());
- sb_frame.SetFrameSP(frame_sp);
- }
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return SBFrame();
+ }
+
+ if (exe_ctx->HasThreadScope()) {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ frame_sp = thread->GetStackFrameAtIndex(idx);
+ if (frame_sp) {
+ thread->SetSelectedFrame(frame_sp.get());
+ sb_frame.SetFrameSP(frame_sp);
}
}
@@ -1202,12 +1259,16 @@ bool SBThread::GetStatus(SBStream &status) const {
Stream &strm = status.ref();
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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 (exe_ctx.HasThreadScope()) {
- exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true,
- /*show_hidden=*/true);
+ if (exe_ctx->HasThreadScope()) {
+ exe_ctx->GetThreadPtr()->GetStatus(strm, 0, 1, 1, true,
+ /*show_hidden=*/true);
} else
strm.PutCString("No status");
@@ -1225,11 +1286,15 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
Stream &strm = description.ref();
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ 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 (exe_ctx.HasThreadScope()) {
- exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(
+ if (exe_ctx->HasThreadScope()) {
+ exe_ctx->GetThreadPtr()->DumpUsingSettingsFormat(
strm, LLDB_INVALID_THREAD_ID, stop_format);
} else
strm.PutCString("No value");
@@ -1247,11 +1312,15 @@ SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
return error;
}
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
+ if (!exe_ctx) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
+ return error;
+ }
- if (exe_ctx.HasThreadScope()) {
- if (exe_ctx.GetThreadPtr()->DumpUsingFormat(
+ if (exe_ctx->HasThreadScope()) {
+ if (exe_ctx->GetThreadPtr()->DumpUsingFormat(
strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) {
return error;
}
@@ -1267,17 +1336,15 @@ SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
LLDB_INSTRUMENT_VA(this, type);
- std::unique_lock<std::recursive_mutex> lock;
- ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+ llvm::Expected<StoppedExecutionContext> exe_ctx =
+ GetStoppedExecutionContext(m_opaque_sp);
SBThread sb_origin_thread;
-
- Process::StopLocker stop_locker;
- if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) {
- if (exe_ctx.HasThreadScope()) {
- ThreadSP real_thread(exe_ctx.GetThreadSP());
+ if (exe_ctx) {
+ if (exe_ctx->HasThreadScope()) {
+ ThreadSP real_thread(exe_ctx->GetThreadSP());
if (real_thread) {
ConstString type_const(type);
- Process *process = exe_ctx.GetProcessPtr();
+ Process *process = exe_ctx->GetProcessPtr();
if (process) {
SystemRuntime *runtime = process->GetSystemRuntime();
if (runtime) {
@@ -1293,6 +1360,8 @@ SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
}
}
}
+ } else {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}");
}
return sb_origin_thread;
diff --git a/lldb/source/Breakpoint/BreakpointResolver.cpp b/lldb/source/Breakpoint/BreakpointResolver.cpp
index 91fdff4..4ac4050 100644
--- a/lldb/source/Breakpoint/BreakpointResolver.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolver.cpp
@@ -42,9 +42,9 @@ const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
BreakpointResolver::OptionNames::LastOptionName)] = {
- "AddressOffset", "Exact", "FileName", "Inlines", "Language",
- "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
- "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
+ "AddressOffset", "Exact", "FileName", "Inlines", "Language",
+ "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
+ "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
"SkipPrologue", "SymbolNames"};
const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
@@ -65,8 +65,10 @@ BreakpointResolver::NameToResolverTy(llvm::StringRef name) {
BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt,
const unsigned char resolverTy,
- lldb::addr_t offset)
- : m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
+ lldb::addr_t offset,
+ bool offset_is_insn_count)
+ : m_breakpoint(bkpt), m_offset(offset),
+ m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {}
BreakpointResolver::~BreakpointResolver() = default;
@@ -364,7 +366,32 @@ void BreakpointResolver::AddLocation(SearchFilter &filter,
BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
bool *new_location) {
- loc_addr.Slide(m_offset);
+ if (m_offset_is_insn_count) {
+ Target &target = GetBreakpoint()->GetTarget();
+ llvm::Expected<DisassemblerSP> expected_instructions =
+ target.ReadInstructions(loc_addr, m_offset);
+ if (!expected_instructions) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints),
+ expected_instructions.takeError(),
+ "error: Unable to read instructions at address 0x{0:x}",
+ loc_addr.GetLoadAddress(&target));
+ return BreakpointLocationSP();
+ }
+
+ const DisassemblerSP instructions = *expected_instructions;
+ if (!instructions ||
+ instructions->GetInstructionList().GetSize() != m_offset) {
+ LLDB_LOG(GetLog(LLDBLog::Breakpoints),
+ "error: Unable to read {0} instructions at address 0x{1:x}",
+ m_offset, loc_addr.GetLoadAddress(&target));
+ return BreakpointLocationSP();
+ }
+
+ loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize());
+ } else {
+ loc_addr.Slide(m_offset);
+ }
+
return GetBreakpoint()->AddLocation(loc_addr, new_location);
}
diff --git a/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
index 828647c..70360d9 100644
--- a/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
@@ -133,6 +133,11 @@ Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback(
Address tmp_address;
if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
m_addr = tmp_address;
+ else
+ return Searcher::eCallbackReturnStop;
+ } else {
+ // If we didn't find the module, then we can't resolve the address.
+ return Searcher::eCallbackReturnStop;
}
}
diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp
index 21024a4..6372595 100644
--- a/lldb/source/Breakpoint/BreakpointResolverName.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp
@@ -24,11 +24,13 @@
using namespace lldb;
using namespace lldb_private;
-BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
- const char *name_cstr, FunctionNameType name_type_mask,
- LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset,
+BreakpointResolverName::BreakpointResolverName(
+ const BreakpointSP &bkpt, const char *name_cstr,
+ FunctionNameType name_type_mask, LanguageType language,
+ Breakpoint::MatchType type, lldb::addr_t offset, bool offset_is_insn_count,
bool skip_prologue)
- : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset,
+ offset_is_insn_count),
m_match_type(type), m_language(language), m_skip_prologue(skip_prologue) {
if (m_match_type == Breakpoint::Regexp) {
m_regex = RegularExpression(name_cstr);
@@ -81,7 +83,7 @@ BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
BreakpointResolverName::BreakpointResolverName(
const BreakpointResolverName &rhs)
: BreakpointResolver(rhs.GetBreakpoint(), BreakpointResolver::NameResolver,
- rhs.GetOffset()),
+ rhs.GetOffset(), rhs.GetOffsetIsInsnCount()),
m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name),
m_regex(rhs.m_regex), m_match_type(rhs.m_match_type),
m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {}
@@ -177,7 +179,8 @@ BreakpointResolverSP BreakpointResolverName::CreateFromStructuredData(
std::shared_ptr<BreakpointResolverName> resolver_sp =
std::make_shared<BreakpointResolverName>(
nullptr, names[0].c_str(), name_masks[0], language,
- Breakpoint::MatchType::Exact, offset, skip_prologue);
+ Breakpoint::MatchType::Exact, offset,
+ /*offset_is_insn_count = */ false, skip_prologue);
for (size_t i = 1; i < num_elem; i++) {
resolver_sp->AddNameLookup(ConstString(names[i]), name_masks[i]);
}
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index d7589cc..0d9eb45 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -18,14 +18,11 @@
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Utility/ConstString.h"
-#include "lldb/Utility/LLDBLog.h"
-#include "lldb/Utility/Log.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
#include <regex>
@@ -93,7 +90,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
dump_options.SetHideRootName(suppress_result)
.SetExpandPointerTypeFlags(lldb::eTypeIsObjC);
- bool is_po = m_varobj_options.use_objc;
+ bool is_po = m_varobj_options.use_object_desc;
StackFrame *frame = m_exe_ctx.GetFramePtr();
@@ -135,22 +132,27 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
};
// Dump `valobj` according to whether `po` was requested or not.
- auto dump_val_object = [&](ValueObject &valobj) -> Error {
+ auto dump_val_object = [&](ValueObject &valobj) {
if (is_po) {
StreamString temp_result_stream;
- if (Error err = valobj.Dump(temp_result_stream, dump_options))
- return err;
+ if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) {
+ result.AppendError(toString(std::move(error)));
+ return;
+ }
llvm::StringRef output = temp_result_stream.GetString();
maybe_add_hint(output);
result.GetOutputStream() << output;
} else {
- if (Error err = valobj.Dump(result.GetOutputStream(), dump_options))
- return err;
+ llvm::Error error =
+ valobj.Dump(result.GetOutputStream(), dump_options);
+ if (error) {
+ result.AppendError(toString(std::move(error)));
+ return;
+ }
}
m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(),
m_cmd_name);
result.SetStatus(eReturnStatusSuccessFinishResult);
- return Error::success();
};
// First, try `expr` as a _limited_ frame variable expression path: only the
@@ -184,13 +186,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
expr);
}
- Error err = dump_val_object(*valobj_sp);
- if (!err)
- return;
-
- // Dump failed, continue on to expression evaluation.
- LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err),
- "could not print frame variable '{1}': {0}", expr);
+ dump_val_object(*valobj_sp);
+ return;
}
}
@@ -199,14 +196,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
if (auto *state = target.GetPersistentExpressionStateForLanguage(language))
if (auto var_sp = state->GetVariable(expr))
if (auto valobj_sp = var_sp->GetValueObject()) {
- Error err = dump_val_object(*valobj_sp);
- if (!err)
- return;
-
- // Dump failed, continue on to expression evaluation.
- LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err),
- "could not print persistent variable '{1}': {0}",
- expr);
+ dump_val_object(*valobj_sp);
+ return;
}
// Third, and lastly, try `expr` as a source expression to evaluate.
@@ -257,12 +248,10 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr);
}
- if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) {
- if (Error err = dump_val_object(*valobj_sp))
- result.SetError(std::move(err));
- } else {
+ if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
+ dump_val_object(*valobj_sp);
+ else
result.SetStatus(eReturnStatusSuccessFinishNoResult);
- }
if (suppress_result)
if (auto result_var_sp =
diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp
index 70e687e..c0553d2 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 4fbcd72..eed44ad 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/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp
index c5b9167..197bffe9 100644
--- a/lldb/source/Commands/CommandObjectExpression.cpp
+++ b/lldb/source/Commands/CommandObjectExpression.cpp
@@ -202,7 +202,7 @@ EvaluateExpressionOptions
CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions(
const Target &target, const OptionGroupValueObjectDisplay &display_opts) {
EvaluateExpressionOptions options;
- options.SetCoerceToId(display_opts.use_objc);
+ options.SetCoerceToId(display_opts.use_object_desc);
options.SetUnwindOnError(unwind_on_error);
options.SetIgnoreBreakpoints(ignore_breakpoints);
options.SetKeepInMemory(true);
@@ -241,11 +241,11 @@ CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions(
bool CommandObjectExpression::CommandOptions::ShouldSuppressResult(
const OptionGroupValueObjectDisplay &display_opts) const {
// Explicitly disabling persistent results takes precedence over the
- // m_verbosity/use_objc logic.
+ // m_verbosity/use_object_desc logic.
if (suppress_persistent_result != eLazyBoolCalculate)
return suppress_persistent_result == eLazyBoolYes;
- return display_opts.use_objc &&
+ return display_opts.use_object_desc &&
m_verbosity == eLanguageRuntimeDescriptionDisplayVerbosityCompact;
}
@@ -332,7 +332,7 @@ Options *CommandObjectExpression::GetOptions() { return &m_option_group; }
void CommandObjectExpression::HandleCompletion(CompletionRequest &request) {
EvaluateExpressionOptions options;
- options.SetCoerceToId(m_varobj_options.use_objc);
+ options.SetCoerceToId(m_varobj_options.use_object_desc);
options.SetLanguage(m_command_options.language);
options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever);
options.SetAutoApplyFixIts(false);
diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp
index 5692699..7e58a95 100644
--- a/lldb/source/Commands/CommandObjectFrame.cpp
+++ b/lldb/source/Commands/CommandObjectFrame.cpp
@@ -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/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp
index f11e27f..c5ab9e9 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/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp
index 7bbb0dd..126f57c 100644
--- a/lldb/source/Commands/CommandObjectSettings.cpp
+++ b/lldb/source/Commands/CommandObjectSettings.cpp
@@ -237,28 +237,62 @@ private:
};
// CommandObjectSettingsShow -- Show current values
+#define LLDB_OPTIONS_settings_show
+#include "CommandOptions.inc"
class CommandObjectSettingsShow : public CommandObjectParsed {
public:
CommandObjectSettingsShow(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "settings show",
"Show matching debugger settings and their current "
- "values. Defaults to showing all settings.",
- nullptr) {
+ "values. Defaults to showing all settings.") {
AddSimpleArgumentList(eArgTypeSettingVariableName, eArgRepeatOptional);
}
~CommandObjectSettingsShow() override = default;
+ Options *GetOptions() override { return &m_options; }
+
+ class CommandOptions : public Options {
+ public:
+ ~CommandOptions() override = default;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override {
+ const int short_option = m_getopt_table[option_idx].val;
+ switch (short_option) {
+ case 'd':
+ m_include_defaults = true;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return {};
+ }
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override {
+ m_include_defaults = false;
+ }
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
+ return g_settings_show_options;
+ }
+
+ bool m_include_defaults = false;
+ };
+
protected:
void DoExecute(Args &args, CommandReturnObject &result) override {
result.SetStatus(eReturnStatusSuccessFinishResult);
+ uint32_t dump_mask = OptionValue::eDumpGroupValue;
+ if (m_options.m_include_defaults)
+ dump_mask |= OptionValue::eDumpOptionDefaultValue;
+
if (!args.empty()) {
for (const auto &arg : args) {
Status error(GetDebugger().DumpPropertyValue(
- &m_exe_ctx, result.GetOutputStream(), arg.ref(),
- OptionValue::eDumpGroupValue));
+ &m_exe_ctx, result.GetOutputStream(), arg.ref(), dump_mask));
if (error.Success()) {
result.GetOutputStream().EOL();
} else {
@@ -267,9 +301,12 @@ protected:
}
} else {
GetDebugger().DumpAllPropertyValues(&m_exe_ctx, result.GetOutputStream(),
- OptionValue::eDumpGroupValue);
+ dump_mask);
}
}
+
+private:
+ CommandOptions m_options;
};
// CommandObjectSettingsWrite -- Write settings to file
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index dbebbbd..3ae08de 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -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/Options.td b/lldb/source/Commands/Options.td
index acb7410..4a70e55 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -56,6 +56,11 @@ let Command = "settings clear" in {
Desc<"Clear all settings.">;
}
+let Command = "settings show" in {
+ def setshow_defaults : Option<"defaults", "d">,
+ Desc<"Include default 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.
@@ -79,7 +84,7 @@ let Command = "breakpoint modify" in {
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.">;
+ 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 "
"index matches this argument.">;
@@ -205,7 +210,7 @@ let Command = "breakpoint set" in {
"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 "
+ 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">,
@@ -245,9 +250,12 @@ let Command = "breakpoint delete" in {
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_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.">;
}
let Command = "breakpoint name" in {
@@ -337,8 +345,10 @@ let Command = "disassemble" in {
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_arch
+ : Option<"arch", "A">,
+ Arg<"Architecture">,
+ Desc<"Specify the architecture 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 start disassembling.">;
@@ -361,6 +371,8 @@ let Command = "disassemble" in {
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_variable : Option<"variable", "v">,
+ Desc<"Enable variable disassembly annotations for this invocation.">;
}
let Command = "diagnostics dump" in {
@@ -441,13 +453,17 @@ let Command = "frame recognizer add" in {
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_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 {
@@ -665,9 +681,10 @@ let Command = "platform process list" in {
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_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.">;
}
@@ -737,17 +754,31 @@ let Command = "process launch" in {
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.">;
+ 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 {
@@ -905,8 +936,11 @@ let Command = "source list" in {
"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_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.">;
@@ -1068,12 +1102,14 @@ let Command = "target stop hook add" in {
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_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.">;
}
let Command = "thread backtrace" in {
@@ -1099,12 +1135,16 @@ let Command = "thread step scope" in {
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_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.">;
@@ -1149,9 +1189,13 @@ let Command = "thread jump" in {
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_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]>,
@@ -1373,8 +1417,10 @@ let Command = "type category enable" in {
}
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 {
diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp
index 00227f0..76f8987 100644
--- a/lldb/source/Core/DemangledNameInfo.cpp
+++ b/lldb/source/Core/DemangledNameInfo.cpp
@@ -111,6 +111,11 @@ void TrackingOutputBuffer::finalizeEnd() {
if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second)
NameInfo.ScopeRange.second = NameInfo.ScopeRange.first;
NameInfo.BasenameRange.first = NameInfo.ScopeRange.second;
+
+ // We call anything past the FunctionEncoding the "suffix".
+ // In practice this would be nodes like `DotSuffix` that wrap
+ // a FunctionEncoding.
+ NameInfo.SuffixRange.first = getCurrentPosition();
}
ScopedOverride<unsigned> TrackingOutputBuffer::enterFunctionTypePrinting() {
@@ -138,6 +143,9 @@ void TrackingOutputBuffer::printLeft(const Node &N) {
default:
OutputBuffer::printLeft(N);
}
+
+ // Keep updating suffix until we reach the end.
+ NameInfo.SuffixRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::printRight(const Node &N) {
@@ -151,6 +159,9 @@ void TrackingOutputBuffer::printRight(const Node &N) {
default:
OutputBuffer::printRight(N);
}
+
+ // Keep updating suffix until we reach the end.
+ NameInfo.SuffixRange.second = getCurrentPosition();
}
void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) {
diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp
index 925de2a..f2ed1f7 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.
@@ -1016,6 +1158,16 @@ uint32_t InstructionList::GetMaxOpcocdeByteSize() const {
return max_inst_size;
}
+size_t InstructionList::GetTotalByteSize() const {
+ size_t total_byte_size = 0;
+ collection::const_iterator pos, end;
+ for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end;
+ ++pos) {
+ total_byte_size += (*pos)->GetOpcode().GetByteSize();
+ }
+ return total_byte_size;
+}
+
InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const {
InstructionSP inst_sp;
if (idx < m_instructions.size())
@@ -1063,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/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp
index 4be9f3e..7580b15 100644
--- a/lldb/source/Core/DynamicLoader.cpp
+++ b/lldb/source/Core/DynamicLoader.cpp
@@ -211,7 +211,7 @@ ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress(
if (uuid.IsValid())
prog_str << uuid.GetAsString();
if (value_is_offset == 0 && value != LLDB_INVALID_ADDRESS) {
- prog_str << "at 0x";
+ prog_str << " at 0x";
prog_str.PutHex64(value);
}
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index 3663f43..91b9c00 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -172,9 +172,6 @@ GetItaniumDemangledStr(const char *M) {
TrackingOutputBuffer OB(demangled_cstr, demangled_size);
demangled_cstr = ipd.finishDemangle(&OB);
- // TODO: we should set the SuffixRange inside the TrackingOutputBuffer.
- OB.NameInfo.SuffixRange.first = OB.NameInfo.QualifiersRange.second;
- OB.NameInfo.SuffixRange.second = std::string_view(OB).size();
info = std::move(OB.NameInfo);
assert(demangled_cstr &&
@@ -559,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 d5ddc2b..bc63a41 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -755,6 +755,230 @@ size_t ModuleList::GetIndexForModule(const Module *module) const {
}
namespace {
+/// A wrapper around ModuleList for shared modules. Provides fast lookups for
+/// file-based ModuleSpec queries.
+class SharedModuleList {
+public:
+ /// Finds all the modules matching the module_spec, and adds them to \p
+ /// matching_module_list.
+ void FindModules(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) const {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ // Try map first for performance - if found, skip expensive full list
+ // search.
+ FindModulesInMap(module_spec, matching_module_list);
+ if (!matching_module_list.IsEmpty())
+ return;
+ m_list.FindModules(module_spec, matching_module_list);
+ // Assert that modules were found in the list but not the map, it's
+ // because the module_spec has no filename or the found module has a
+ // different filename. For example, when searching by UUID and finding a
+ // module with an alias.
+ assert((matching_module_list.IsEmpty() ||
+ module_spec.GetFileSpec().GetFilename().IsEmpty() ||
+ module_spec.GetFileSpec().GetFilename() !=
+ matching_module_list.GetModuleAtIndex(0)
+ ->GetFileSpec()
+ .GetFilename()) &&
+ "Search by name not found in SharedModuleList's map");
+ }
+
+ ModuleSP FindModule(const Module &module) {
+
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ if (ModuleSP result = FindModuleInMap(module))
+ return result;
+ return m_list.FindModule(&module);
+ }
+
+ // UUID searches bypass map since UUIDs aren't indexed by filename.
+ ModuleSP FindModule(const UUID &uuid) const {
+ return m_list.FindModule(uuid);
+ }
+
+ void Append(const ModuleSP &module_sp, bool use_notifier) {
+ if (!module_sp)
+ return;
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ m_list.Append(module_sp, use_notifier);
+ AddToMap(module_sp);
+ }
+
+ size_t RemoveOrphans(bool mandatory) {
+ std::unique_lock<std::recursive_mutex> lock(GetMutex(), std::defer_lock);
+ if (mandatory) {
+ lock.lock();
+ } else {
+ if (!lock.try_lock())
+ return 0;
+ }
+ size_t total_count = 0;
+ size_t run_count;
+ do {
+ // Remove indexed orphans first, then remove non-indexed orphans. This
+ // order is important because the shared count will be different if a
+ // module is indexed or not.
+ run_count = RemoveOrphansFromMapAndList();
+ run_count += m_list.RemoveOrphans(mandatory);
+ total_count += run_count;
+ // Because removing orphans might make new orphans, remove from both
+ // containers until a fixed-point is reached.
+ } while (run_count != 0);
+
+ return total_count;
+ }
+
+ bool Remove(const ModuleSP &module_sp, bool use_notifier = true) {
+ if (!module_sp)
+ return false;
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ RemoveFromMap(*module_sp.get());
+ return m_list.Remove(module_sp, use_notifier);
+ }
+
+ void ReplaceEquivalent(const ModuleSP &module_sp,
+ llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ m_list.ReplaceEquivalent(module_sp, old_modules);
+ ReplaceEquivalentInMap(module_sp);
+ }
+
+ bool RemoveIfOrphaned(const Module *module_ptr) {
+ std::lock_guard<std::recursive_mutex> guard(GetMutex());
+ RemoveFromMap(*module_ptr, /*if_orphaned=*/true);
+ return m_list.RemoveIfOrphaned(module_ptr);
+ }
+
+ std::recursive_mutex &GetMutex() const { return m_list.GetMutex(); }
+
+private:
+ ModuleSP FindModuleInMap(const Module &module) const {
+ if (!module.GetFileSpec().GetFilename())
+ return ModuleSP();
+ ConstString name = module.GetFileSpec().GetFilename();
+ auto it = m_name_to_modules.find(name);
+ if (it == m_name_to_modules.end())
+ return ModuleSP();
+ const llvm::SmallVectorImpl<ModuleSP> &vector = it->second;
+ for (const ModuleSP &module_sp : vector) {
+ if (module_sp.get() == &module)
+ return module_sp;
+ }
+ return ModuleSP();
+ }
+
+ void FindModulesInMap(const ModuleSpec &module_spec,
+ ModuleList &matching_module_list) const {
+ auto it = m_name_to_modules.find(module_spec.GetFileSpec().GetFilename());
+ if (it == m_name_to_modules.end())
+ return;
+ const llvm::SmallVectorImpl<ModuleSP> &vector = it->second;
+ for (const ModuleSP &module_sp : vector) {
+ if (module_sp->MatchesModuleSpec(module_spec))
+ matching_module_list.Append(module_sp);
+ }
+ }
+
+ void AddToMap(const ModuleSP &module_sp) {
+ ConstString name = module_sp->GetFileSpec().GetFilename();
+ if (name.IsEmpty())
+ return;
+ m_name_to_modules[name].push_back(module_sp);
+ }
+
+ void RemoveFromMap(const Module &module, bool if_orphaned = false) {
+ ConstString name = module.GetFileSpec().GetFilename();
+ if (!m_name_to_modules.contains(name))
+ return;
+ llvm::SmallVectorImpl<ModuleSP> &vec = m_name_to_modules[name];
+ for (auto *it = vec.begin(); it != vec.end(); ++it) {
+ if (it->get() == &module) {
+ if (!if_orphaned || it->use_count() == kUseCountOrphaned) {
+ vec.erase(it);
+ break;
+ }
+ }
+ }
+ }
+
+ void ReplaceEquivalentInMap(const ModuleSP &module_sp) {
+ RemoveEquivalentModulesFromMap(module_sp);
+ AddToMap(module_sp);
+ }
+
+ void RemoveEquivalentModulesFromMap(const ModuleSP &module_sp) {
+ ConstString name = module_sp->GetFileSpec().GetFilename();
+ if (name.IsEmpty())
+ return;
+
+ auto it = m_name_to_modules.find(name);
+ if (it == m_name_to_modules.end())
+ return;
+
+ // First remove any equivalent modules. Equivalent modules are modules
+ // whose path, platform path and architecture match.
+ ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(),
+ module_sp->GetArchitecture());
+ equivalent_module_spec.GetPlatformFileSpec() =
+ module_sp->GetPlatformFileSpec();
+
+ llvm::SmallVectorImpl<ModuleSP> &vec = it->second;
+ llvm::erase_if(vec, [&equivalent_module_spec](ModuleSP &element) {
+ return element->MatchesModuleSpec(equivalent_module_spec);
+ });
+ }
+
+ /// Remove orphans from the vector and return the removed modules.
+ ModuleList RemoveOrphansFromVector(llvm::SmallVectorImpl<ModuleSP> &vec) {
+ // remove_if moves the elements that match the condition to the end of the
+ // container, and returns an iterator to the first element that was moved.
+ auto *to_remove_start = llvm::remove_if(vec, [](const ModuleSP &module) {
+ return module.use_count() == kUseCountOrphaned;
+ });
+
+ ModuleList to_remove;
+ for (ModuleSP *it = to_remove_start; it != vec.end(); ++it)
+ to_remove.Append(*it);
+
+ vec.erase(to_remove_start, vec.end());
+ return to_remove;
+ }
+
+ /// Remove orphans that exist in both the map and list. This does not remove
+ /// any orphans that exist exclusively on the list.
+ ///
+ /// The mutex must be locked by the caller.
+ int RemoveOrphansFromMapAndList() {
+ // Modules might hold shared pointers to other modules, so removing one
+ // module might orphan other modules. Keep removing modules until
+ // there are no further modules that can be removed.
+ int remove_count = 0;
+ int previous_remove_count;
+ do {
+ previous_remove_count = remove_count;
+ for (auto &[name, vec] : m_name_to_modules) {
+ if (vec.empty())
+ continue;
+ ModuleList to_remove = RemoveOrphansFromVector(vec);
+ remove_count += to_remove.GetSize();
+ m_list.Remove(to_remove);
+ }
+ // Break when fixed-point is reached.
+ } while (previous_remove_count != remove_count);
+
+ return remove_count;
+ }
+
+ ModuleList m_list;
+
+ /// A hash map from a module's filename to all the modules that share that
+ /// filename, for fast module lookups by name.
+ llvm::DenseMap<ConstString, llvm::SmallVector<ModuleSP, 1>> m_name_to_modules;
+
+ /// The use count of a module held only by m_list and m_name_to_modules.
+ static constexpr long kUseCountOrphaned = 2;
+};
+
struct SharedModuleListInfo {
ModuleList module_list;
ModuleListProperties module_list_properties;
diff --git a/lldb/source/Core/ProtocolServer.cpp b/lldb/source/Core/ProtocolServer.cpp
index 41636cd..38668f3 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/Core/Section.cpp b/lldb/source/Core/Section.cpp
index 27dcf98..02d9d86 100644
--- a/lldb/source/Core/Section.cpp
+++ b/lldb/source/Core/Section.cpp
@@ -153,6 +153,8 @@ const char *Section::GetTypeAsCString() const {
return "lldb-formatters";
case eSectionTypeSwiftModules:
return "swift-modules";
+ case eSectionTypeWasmName:
+ return "wasm-name";
case eSectionTypeOther:
return "regular";
}
@@ -415,6 +417,7 @@ bool Section::ContainsOnlyDebugInfo() const {
case eSectionTypeCompactUnwind:
case eSectionTypeGoSymtab:
case eSectionTypeAbsoluteAddress:
+ case eSectionTypeWasmName:
case eSectionTypeOther:
// Used for "__dof_cache" in mach-o or ".debug" for COFF which isn't debug
// information that we parse at all. This was causing system files with no
diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
index c91b3f8..f9e65d3 100644
--- a/lldb/source/Core/Value.cpp
+++ b/lldb/source/Core/Value.cpp
@@ -347,15 +347,12 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
else
data.SetAddressByteSize(sizeof(void *));
- uint32_t limit_byte_size = UINT32_MAX;
+ if (!type_size)
+ return Status::FromErrorString("type does not have a size");
- if (type_size)
- limit_byte_size = *type_size;
-
- if (limit_byte_size <= m_value.GetByteSize()) {
- if (m_value.GetData(data, limit_byte_size))
- return error; // Success;
- }
+ uint32_t result_byte_size = *type_size;
+ if (m_value.GetData(data, result_byte_size))
+ return error; // Success;
error = Status::FromErrorString("extracting data from value failed");
break;
@@ -494,9 +491,11 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
address_type = eAddressTypeHost;
if (exe_ctx) {
- Target *target = exe_ctx->GetTargetPtr();
- if (target) {
- data.SetByteOrder(target->GetArchitecture().GetByteOrder());
+ if (Target *target = exe_ctx->GetTargetPtr()) {
+ // Registers are always stored in host endian.
+ data.SetByteOrder(m_context_type == ContextType::RegisterInfo
+ ? endian::InlHostByteOrder()
+ : target->GetArchitecture().GetByteOrder());
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
break;
}
diff --git a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp
index c343e60..e1df952 100644
--- a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp
+++ b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp
@@ -17,7 +17,7 @@ DumpValueObjectOptions::DumpValueObjectOptions()
: m_summary_sp(), m_root_valobj_name(), m_decl_printing_helper(),
m_child_printing_decider(), m_pointer_as_array(), m_use_synthetic(true),
m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false),
- m_show_types(false), m_show_location(false), m_use_objc(false),
+ m_show_types(false), m_show_location(false), m_use_object_desc(false),
m_hide_root_type(false), m_hide_root_name(false), m_hide_name(false),
m_hide_value(false), m_run_validator(false),
m_use_type_display_name(true), m_allow_oneliner_mode(true),
@@ -65,8 +65,19 @@ DumpValueObjectOptions &DumpValueObjectOptions::SetShowLocation(bool show) {
return *this;
}
-DumpValueObjectOptions &DumpValueObjectOptions::SetUseObjectiveC(bool use) {
- m_use_objc = use;
+DumpValueObjectOptions &DumpValueObjectOptions::DisableObjectDescription() {
+ // Reset these options to their default values.
+ SetUseObjectDescription(false);
+ SetHideRootType(false);
+ SetHideName(false);
+ SetHideValue(false);
+ SetShowSummary(true);
+ return *this;
+}
+
+DumpValueObjectOptions &
+DumpValueObjectOptions::SetUseObjectDescription(bool use) {
+ m_use_object_desc = use;
return *this;
}
diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
index c2f8bb3..e200511 100644
--- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp
+++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
@@ -14,9 +14,11 @@
#include "lldb/Target/Target.h"
#include "lldb/Utility/Stream.h"
#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/MathExtras.h"
#include <cstdint>
#include <memory>
+#include <optional>
using namespace lldb;
using namespace lldb_private;
@@ -69,6 +71,18 @@ void ValueObjectPrinter::Init(
SetupMostSpecializedValue();
}
+static const char *maybeNewline(const std::string &s) {
+ // If the string already ends with a \n don't add another one.
+ if (s.empty() || s.back() != '\n')
+ return "\n";
+ return "";
+}
+
+bool ValueObjectPrinter::ShouldPrintObjectDescription() {
+ return ShouldPrintValueObject() && m_options.m_use_object_desc && !IsNil() &&
+ !IsUninitialized() && !m_options.m_pointer_as_array;
+}
+
llvm::Error ValueObjectPrinter::PrintValueObject() {
// If the incoming ValueObject is in an error state, the best we're going to
// get out of it is its type. But if we don't even have that, just print
@@ -77,6 +91,25 @@ llvm::Error ValueObjectPrinter::PrintValueObject() {
!m_orig_valobj.GetCompilerType().IsValid())
return m_orig_valobj.GetError().ToError();
+ std::optional<std::string> object_desc;
+ if (ShouldPrintObjectDescription()) {
+ // The object description is invoked now, but not printed until after
+ // value/summary. Calling GetObjectDescription at the outset of printing
+ // allows for early discovery of errors. In the case of an error, the value
+ // object is printed normally.
+ llvm::Expected<std::string> object_desc_or_err =
+ GetMostSpecializedValue().GetObjectDescription();
+ if (!object_desc_or_err) {
+ auto error_msg = toString(object_desc_or_err.takeError());
+ *m_stream << "error: " << error_msg << maybeNewline(error_msg);
+
+ // Print the value object directly.
+ m_options.DisableObjectDescription();
+ } else {
+ object_desc = *object_desc_or_err;
+ }
+ }
+
if (ShouldPrintValueObject()) {
PrintLocationIfNeeded();
m_stream->Indent();
@@ -90,8 +123,10 @@ llvm::Error ValueObjectPrinter::PrintValueObject() {
m_val_summary_ok =
PrintValueAndSummaryIfNeeded(value_printed, summary_printed);
- if (m_val_summary_ok)
+ if (m_val_summary_ok) {
+ PrintObjectDescriptionIfNeeded(object_desc);
return PrintChildrenIfNeeded(value_printed, summary_printed);
+ }
m_stream->EOL();
return llvm::Error::success();
@@ -144,24 +179,6 @@ void ValueObjectPrinter::SetupMostSpecializedValue() {
"SetupMostSpecialized value must compute a valid ValueObject");
}
-llvm::Expected<std::string> ValueObjectPrinter::GetDescriptionForDisplay() {
- ValueObject &valobj = GetMostSpecializedValue();
- llvm::Expected<std::string> maybe_str = valobj.GetObjectDescription();
- if (maybe_str)
- return maybe_str;
-
- const char *str = nullptr;
- if (!str)
- str = valobj.GetSummaryAsCString();
- if (!str)
- str = valobj.GetValueAsCString();
-
- if (!str)
- return maybe_str;
- llvm::consumeError(maybe_str.takeError());
- return str;
-}
-
const char *ValueObjectPrinter::GetRootNameForDisplay() {
const char *root_valobj_name =
m_options.m_root_valobj_name.empty()
@@ -468,38 +485,14 @@ bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed,
return !error_printed;
}
-llvm::Error
-ValueObjectPrinter::PrintObjectDescriptionIfNeeded(bool value_printed,
- bool summary_printed) {
- if (ShouldPrintValueObject()) {
- // let's avoid the overly verbose no description error for a nil thing
- if (m_options.m_use_objc && !IsNil() && !IsUninitialized() &&
- (!m_options.m_pointer_as_array)) {
- if (!m_options.m_hide_value || ShouldShowName())
- *m_stream << ' ';
- llvm::Expected<std::string> object_desc =
- (value_printed || summary_printed)
- ? GetMostSpecializedValue().GetObjectDescription()
- : GetDescriptionForDisplay();
- if (!object_desc) {
- // If no value or summary was printed, surface the error.
- if (!value_printed && !summary_printed)
- return object_desc.takeError();
- // Otherwise gently nudge the user that they should have used
- // `p` instead of `po`. Unfortunately we cannot be more direct
- // about this, because we don't actually know what the user did.
- *m_stream << "warning: no object description available\n";
- llvm::consumeError(object_desc.takeError());
- } else {
- *m_stream << *object_desc;
- // If the description already ends with a \n don't add another one.
- if (object_desc->empty() || object_desc->back() != '\n')
- *m_stream << '\n';
- }
- return llvm::Error::success();
- }
- }
- return llvm::Error::success();
+void ValueObjectPrinter::PrintObjectDescriptionIfNeeded(
+ std::optional<std::string> object_desc) {
+ if (!object_desc)
+ return;
+
+ if (!m_options.m_hide_value || ShouldShowName())
+ *m_stream << ' ';
+ *m_stream << *object_desc << maybeNewline(*object_desc);
}
bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const {
@@ -524,7 +517,7 @@ bool ValueObjectPrinter::ShouldPrintChildren(
if (m_options.m_pointer_as_array)
return true;
- if (m_options.m_use_objc)
+ if (m_options.m_use_object_desc)
return false;
bool print_children = true;
@@ -819,9 +812,6 @@ bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) {
llvm::Error ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed,
bool summary_printed) {
- auto error = PrintObjectDescriptionIfNeeded(value_printed, summary_printed);
- if (error)
- return error;
ValueObject &valobj = GetMostSpecializedValue();
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index ed4e4e4..df56bcf 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; }
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 5e40df2..d557084 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -737,8 +737,9 @@ public:
// If that didn't work, try the function.
if (load_address == LLDB_INVALID_ADDRESS && candidate_sc.function) {
Address addr = candidate_sc.function->GetAddress();
- load_address = m_target.GetProcessSP() ? addr.GetLoadAddress(&m_target)
- : addr.GetFileAddress();
+ load_address = m_target.GetProcessSP()
+ ? addr.GetCallableLoadAddress(&m_target)
+ : addr.GetFileAddress();
}
// We found a load address.
@@ -798,7 +799,7 @@ ResolveFunctionCallLabel(const FunctionCallLabel &label,
auto sc_or_err = symbol_file->ResolveFunctionCallLabel(label);
if (!sc_or_err)
return llvm::joinErrors(
- llvm::createStringError("failed to resolve function by UID"),
+ llvm::createStringError("failed to resolve function by UID:"),
sc_or_err.takeError());
SymbolContextList sc_list;
diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp
index 329768d..771a9ab 100644
--- a/lldb/source/Expression/Materializer.cpp
+++ b/lldb/source/Expression/Materializer.cpp
@@ -1377,29 +1377,26 @@ public:
return;
}
- DataExtractor register_data;
-
- if (!reg_value.GetData(register_data)) {
- err = Status::FromErrorStringWithFormat(
- "couldn't get the data for register %s", m_register_info.name);
- return;
- }
-
- if (register_data.GetByteSize() != m_register_info.byte_size) {
+ if (reg_value.GetByteSize() != m_register_info.byte_size) {
err = Status::FromErrorStringWithFormat(
"data for register %s had size %llu but we expected %llu",
- m_register_info.name, (unsigned long long)register_data.GetByteSize(),
+ m_register_info.name, (unsigned long long)reg_value.GetByteSize(),
(unsigned long long)m_register_info.byte_size);
return;
}
- m_register_contents = std::make_shared<DataBufferHeap>(
- register_data.GetDataStart(), register_data.GetByteSize());
+ lldb_private::DataBufferHeap buf(reg_value.GetByteSize(), 0);
+ reg_value.GetAsMemoryData(m_register_info, buf.GetBytes(),
+ buf.GetByteSize(), map.GetByteOrder(), err);
+ if (!err.Success())
+ return;
+
+ m_register_contents = std::make_shared<DataBufferHeap>(buf);
Status write_error;
- map.WriteMemory(load_addr, register_data.GetDataStart(),
- register_data.GetByteSize(), write_error);
+ map.WriteMemory(load_addr, buf.GetBytes(), reg_value.GetByteSize(),
+ write_error);
if (!write_error.Success()) {
err = Status::FromErrorStringWithFormat(
diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp
index e5377d3..92017d2 100644
--- a/lldb/source/Expression/REPL.cpp
+++ b/lldb/source/Expression/REPL.cpp
@@ -321,7 +321,7 @@ void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
EvaluateExpressionOptions expr_options = m_expr_options;
- expr_options.SetCoerceToId(m_varobj_options.use_objc);
+ expr_options.SetCoerceToId(m_varobj_options.use_object_desc);
expr_options.SetKeepInMemory(true);
expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
expr_options.SetGenerateDebugInfo(true);
diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt
index b15d72e..c9e8afe 100644
--- a/lldb/source/Host/CMakeLists.txt
+++ b/lldb/source/Host/CMakeLists.txt
@@ -7,7 +7,7 @@ if (APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY)
endif()
endif()
-if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX")
+if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
add_definitions("-D_ALL_SOURCE")
endif()
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 5ed30fb..1fc86c8a 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"
@@ -219,20 +217,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();
}
}
diff --git a/lldb/source/Host/common/HostInfoBase.cpp b/lldb/source/Host/common/HostInfoBase.cpp
index 89dfe4a..a02ac77 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/common/JSONTransport.cpp b/lldb/source/Host/common/JSONTransport.cpp
index 546c12c..c4b42ea 100644
--- a/lldb/source/Host/common/JSONTransport.cpp
+++ b/lldb/source/Host/common/JSONTransport.cpp
@@ -7,171 +7,26 @@
//===----------------------------------------------------------------------===//
#include "lldb/Host/JSONTransport.h"
-#include "lldb/Utility/IOObject.h"
-#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
-#include "lldb/Utility/SelectHelper.h"
#include "lldb/Utility/Status.h"
-#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
-#include <optional>
#include <string>
-#include <utility>
using namespace llvm;
using namespace lldb;
using namespace lldb_private;
-/// ReadFull attempts to read the specified number of bytes. If EOF is
-/// encountered, an empty string is returned.
-static Expected<std::string>
-ReadFull(IOObject &descriptor, size_t length,
- std::optional<std::chrono::microseconds> timeout = std::nullopt) {
- if (!descriptor.IsValid())
- return llvm::make_error<TransportInvalidError>();
+char TransportUnhandledContentsError::ID;
- bool timeout_supported = true;
- // FIXME: SelectHelper does not work with NativeFile on Win32.
-#if _WIN32
- timeout_supported = descriptor.GetFdType() == IOObject::eFDTypeSocket;
-#endif
+TransportUnhandledContentsError::TransportUnhandledContentsError(
+ std::string unhandled_contents)
+ : m_unhandled_contents(unhandled_contents) {}
- if (timeout && timeout_supported) {
- SelectHelper sh;
- sh.SetTimeout(*timeout);
- sh.FDSetRead(
- reinterpret_cast<lldb::socket_t>(descriptor.GetWaitableHandle()));
- Status status = sh.Select();
- if (status.Fail()) {
- // Convert timeouts into a specific error.
- if (status.GetType() == lldb::eErrorTypePOSIX &&
- status.GetError() == ETIMEDOUT)
- return make_error<TransportTimeoutError>();
- return status.takeError();
- }
- }
-
- std::string data;
- data.resize(length);
- Status status = descriptor.Read(data.data(), length);
- if (status.Fail())
- return status.takeError();
-
- // Read returns '' on EOF.
- if (length == 0)
- return make_error<TransportEOFError>();
-
- // Return the actual number of bytes read.
- return data.substr(0, length);
-}
-
-static Expected<std::string>
-ReadUntil(IOObject &descriptor, StringRef delimiter,
- std::optional<std::chrono::microseconds> timeout = std::nullopt) {
- std::string buffer;
- buffer.reserve(delimiter.size() + 1);
- while (!llvm::StringRef(buffer).ends_with(delimiter)) {
- Expected<std::string> next =
- ReadFull(descriptor, buffer.empty() ? delimiter.size() : 1, timeout);
- if (auto Err = next.takeError())
- return std::move(Err);
- buffer += *next;
- }
- return buffer.substr(0, buffer.size() - delimiter.size());
-}
-
-JSONTransport::JSONTransport(IOObjectSP input, IOObjectSP output)
- : m_input(std::move(input)), m_output(std::move(output)) {}
-
-void JSONTransport::Log(llvm::StringRef message) {
- LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message);
+void TransportUnhandledContentsError::log(llvm::raw_ostream &OS) const {
+ OS << "transport EOF with unhandled contents: '" << m_unhandled_contents
+ << "'";
}
-
-Expected<std::string>
-HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) {
- if (!m_input || !m_input->IsValid())
- return llvm::make_error<TransportInvalidError>();
-
- IOObject *input = m_input.get();
- Expected<std::string> message_header =
- ReadFull(*input, kHeaderContentLength.size(), timeout);
- if (!message_header)
- return message_header.takeError();
- if (*message_header != kHeaderContentLength)
- return createStringError(formatv("expected '{0}' and got '{1}'",
- kHeaderContentLength, *message_header)
- .str());
-
- Expected<std::string> raw_length = ReadUntil(*input, kHeaderSeparator);
- if (!raw_length)
- return handleErrors(raw_length.takeError(),
- [&](const TransportEOFError &E) -> llvm::Error {
- return createStringError(
- "unexpected EOF while reading header separator");
- });
-
- size_t length;
- if (!to_integer(*raw_length, length))
- return createStringError(
- formatv("invalid content length {0}", *raw_length).str());
-
- Expected<std::string> raw_json = ReadFull(*input, length);
- if (!raw_json)
- return handleErrors(
- raw_json.takeError(), [&](const TransportEOFError &E) -> llvm::Error {
- return createStringError("unexpected EOF while reading JSON");
- });
-
- Log(llvm::formatv("--> {0}", *raw_json).str());
-
- return raw_json;
+std::error_code TransportUnhandledContentsError::convertToErrorCode() const {
+ return std::make_error_code(std::errc::bad_message);
}
-
-Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) {
- if (!m_output || !m_output->IsValid())
- return llvm::make_error<TransportInvalidError>();
-
- Log(llvm::formatv("<-- {0}", message).str());
-
- std::string Output;
- raw_string_ostream OS(Output);
- OS << kHeaderContentLength << message.length() << kHeaderSeparator << message;
- size_t num_bytes = Output.size();
- return m_output->Write(Output.data(), num_bytes).takeError();
-}
-
-Expected<std::string>
-JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) {
- if (!m_input || !m_input->IsValid())
- return make_error<TransportInvalidError>();
-
- IOObject *input = m_input.get();
- Expected<std::string> raw_json =
- ReadUntil(*input, kMessageSeparator, timeout);
- if (!raw_json)
- return raw_json.takeError();
-
- Log(llvm::formatv("--> {0}", *raw_json).str());
-
- return *raw_json;
-}
-
-Error JSONRPCTransport::WriteImpl(const std::string &message) {
- if (!m_output || !m_output->IsValid())
- return llvm::make_error<TransportInvalidError>();
-
- Log(llvm::formatv("<-- {0}", message).str());
-
- std::string Output;
- llvm::raw_string_ostream OS(Output);
- OS << message << kMessageSeparator;
- size_t num_bytes = Output.size();
- return m_output->Write(Output.data(), num_bytes).takeError();
-}
-
-char TransportEOFError::ID;
-char TransportTimeoutError::ID;
-char TransportInvalidError::ID;
diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index 61f9419..79e1322 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 4e747f7..4277b8e 100644
--- a/lldb/source/Host/windows/Host.cpp
+++ b/lldb/source/Host/windows/Host.cpp
@@ -22,6 +22,7 @@
#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"
@@ -32,6 +33,8 @@
using namespace lldb;
using namespace lldb_private;
+using llvm::sys::windows::UTF8ToUTF16;
+
static bool GetTripleForProcess(const FileSpec &executable,
llvm::Triple &triple) {
// Open the PE File as a binary file, and parse just enough information to
@@ -325,31 +328,18 @@ private:
static llvm::ManagedStatic<WindowsEventLog> event_log;
-static std::wstring AnsiToUtf16(const std::string &ansi) {
- if (ansi.empty())
- return {};
-
- const int unicode_length =
- MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, nullptr, 0);
- if (unicode_length == 0)
- return {};
-
- std::wstring unicode(unicode_length, L'\0');
- MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, &unicode[0], unicode_length);
- return unicode;
-}
-
void Host::SystemLog(Severity severity, llvm::StringRef message) {
+ if (message.empty())
+ return;
+
HANDLE h = event_log->GetHandle();
if (!h)
return;
- std::wstring wide_message = AnsiToUtf16(message.str());
- if (wide_message.empty())
+ llvm::SmallVector<wchar_t, 1> argsUTF16;
+ if (UTF8ToUTF16(message.str(), argsUTF16))
return;
- LPCWSTR msg_ptr = wide_message.c_str();
-
WORD event_type;
switch (severity) {
case lldb::eSeverityWarning:
@@ -363,5 +353,7 @@ void Host::SystemLog(Severity severity, llvm::StringRef message) {
event_type = EVENTLOG_INFORMATION_TYPE;
}
- ReportEventW(h, event_type, 0, 0, nullptr, 1, 0, &msg_ptr, nullptr);
+ LPCWSTR messages[1] = {argsUTF16.data()};
+ ReportEventW(h, event_type, 0, 0, nullptr, std::size(messages), 0, messages,
+ nullptr);
}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index a0080cf..d06e8c3 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"
@@ -335,7 +336,7 @@ void CommandInterpreter::Initialize() {
AddAlias("ni", cmd_obj_sp);
}
- cmd_obj_sp = GetCommandSPExact("thread step-in");
+ cmd_obj_sp = GetCommandSPExact("_regexp-step");
if (cmd_obj_sp) {
AddAlias("s", cmd_obj_sp);
AddAlias("step", cmd_obj_sp);
@@ -946,6 +947,27 @@ void CommandInterpreter::LoadCommandDictionary() {
jump_regex_cmd_sp;
}
}
+
+ std::shared_ptr<CommandObjectRegexCommand> step_regex_cmd_sp(
+ new CommandObjectRegexCommand(
+ *this, "_regexp-step",
+ "Single step, optionally to a specific function.",
+ "\n"
+ "_regexp-step // Single step\n"
+ "_regexp-step <function-name> // Step into the named function\n",
+ 0, false));
+ if (step_regex_cmd_sp) {
+ if (step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*$",
+ "thread step-in") &&
+ step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*(-.+)$",
+ "thread step-in %1") &&
+ step_regex_cmd_sp->AddRegexCommand(
+ "^[[:space:]]*(.+)[[:space:]]*$",
+ "thread step-in --end-linenumber block --step-in-target %1")) {
+ m_command_dict[std::string(step_regex_cmd_sp->GetCommandName())] =
+ step_regex_cmd_sp;
+ }
+ }
}
int CommandInterpreter::GetCommandNamesMatchingPartialString(
@@ -2481,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())
@@ -2510,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) {
@@ -2567,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);
@@ -2590,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 129646e..43e19b3 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -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/OptionGroupValueObjectDisplay.cpp b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp
index d633c46..1939d79 100644
--- a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp
+++ b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp
@@ -90,7 +90,7 @@ Status OptionGroupValueObjectDisplay::SetOptionValue(
flat_output = true;
break;
case 'O':
- use_objc = true;
+ use_object_desc = true;
break;
case 'R':
be_raw = true;
@@ -163,7 +163,7 @@ void OptionGroupValueObjectDisplay::OptionParsingStarting(
no_summary_depth = 0;
show_location = false;
flat_output = false;
- use_objc = false;
+ use_object_desc = false;
max_depth = UINT32_MAX;
max_depth_is_default = true;
ptr_depth = 0;
@@ -191,14 +191,14 @@ DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions(
lldb::Format format, lldb::TypeSummaryImplSP summary_sp) {
DumpValueObjectOptions options;
options.SetMaximumPointerDepth(ptr_depth);
- if (use_objc)
+ if (use_object_desc)
options.SetShowSummary(false);
else
options.SetOmitSummaryDepth(no_summary_depth);
options.SetMaximumDepth(max_depth, max_depth_is_default)
.SetShowTypes(show_types)
.SetShowLocation(show_location)
- .SetUseObjectiveC(use_objc)
+ .SetUseObjectDescription(use_object_desc)
.SetUseDynamicType(use_dynamic)
.SetUseSyntheticValue(use_synth)
.SetFlatOutput(flat_output)
@@ -208,8 +208,9 @@ DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions(
if (lang_descr_verbosity ==
eLanguageRuntimeDescriptionDisplayVerbosityCompact)
- options.SetHideRootType(use_objc).SetHideName(use_objc).SetHideValue(
- use_objc);
+ options.SetHideRootType(use_object_desc)
+ .SetHideName(use_object_desc)
+ .SetHideValue(use_object_desc);
if (be_raw)
options.SetRawDisplay();
diff --git a/lldb/source/Interpreter/OptionValueArch.cpp b/lldb/source/Interpreter/OptionValueArch.cpp
index ea15cca..a960e39 100644
--- a/lldb/source/Interpreter/OptionValueArch.cpp
+++ b/lldb/source/Interpreter/OptionValueArch.cpp
@@ -11,6 +11,7 @@
#include "lldb/DataFormatters/FormatManager.h"
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/State.h"
@@ -30,6 +31,12 @@ void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (arch_name)
strm.PutCString(arch_name);
}
+
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value && m_default_value.IsValid()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString(m_default_value.GetArchitectureName());
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueArray.cpp b/lldb/source/Interpreter/OptionValueArray.cpp
index f6c14de..5600333 100644
--- a/lldb/source/Interpreter/OptionValueArray.cpp
+++ b/lldb/source/Interpreter/OptionValueArray.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueArray.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/Stream.h"
@@ -27,8 +28,15 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionValue) {
const bool one_line = dump_mask & eDumpOptionCommand;
const uint32_t size = m_values.size();
- if (dump_mask & eDumpOptionType)
- strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : "");
+ if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) {
+ strm.PutCString(" =");
+ if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString("empty");
+ }
+ if (!m_values.empty() && !one_line)
+ strm.PutCString("\n");
+ }
if (!one_line)
strm.IndentMore();
for (uint32_t i = 0; i < size; ++i) {
diff --git a/lldb/source/Interpreter/OptionValueBoolean.cpp b/lldb/source/Interpreter/OptionValueBoolean.cpp
index d4fda76..023c243 100644
--- a/lldb/source/Interpreter/OptionValueBoolean.cpp
+++ b/lldb/source/Interpreter/OptionValueBoolean.cpp
@@ -10,6 +10,7 @@
#include "lldb/Host/PosixApi.h"
#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StringList.h"
#include "llvm/ADT/STLExtras.h"
@@ -27,6 +28,11 @@ void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx,
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
strm.PutCString(m_current_value ? "true" : "false");
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ strm.PutCString(m_default_value ? "true" : "false");
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueChar.cpp b/lldb/source/Interpreter/OptionValueChar.cpp
index 2aadcff..595dcba 100644
--- a/lldb/source/Interpreter/OptionValueChar.cpp
+++ b/lldb/source/Interpreter/OptionValueChar.cpp
@@ -9,6 +9,7 @@
#include "lldb/Interpreter/OptionValueChar.h"
#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StringList.h"
#include "llvm/ADT/STLExtras.h"
@@ -16,6 +17,13 @@
using namespace lldb;
using namespace lldb_private;
+static void DumpChar(Stream &strm, char value) {
+ if (value != '\0')
+ strm.PutChar(value);
+ else
+ strm.PutCString("(null)");
+}
+
void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
uint32_t dump_mask) {
if (dump_mask & eDumpOptionType)
@@ -24,10 +32,12 @@ void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionValue) {
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
- if (m_current_value != '\0')
- strm.PutChar(m_current_value);
- else
- strm.PutCString("(null)");
+ DumpChar(strm, m_current_value);
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ DumpChar(strm, m_default_value);
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueDictionary.cpp b/lldb/source/Interpreter/OptionValueDictionary.cpp
index 19e21dd..0efc76b 100644
--- a/lldb/source/Interpreter/OptionValueDictionary.cpp
+++ b/lldb/source/Interpreter/OptionValueDictionary.cpp
@@ -9,6 +9,7 @@
#include "lldb/Interpreter/OptionValueDictionary.h"
#include "lldb/DataFormatters/FormatManager.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Interpreter/OptionValueEnumeration.h"
#include "lldb/Interpreter/OptionValueString.h"
#include "lldb/Utility/Args.h"
@@ -30,8 +31,13 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx,
}
if (dump_mask & eDumpOptionValue) {
const bool one_line = dump_mask & eDumpOptionCommand;
- if (dump_mask & eDumpOptionType)
+ if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) {
strm.PutCString(" =");
+ if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString("empty");
+ }
+ }
if (!one_line)
strm.IndentMore();
diff --git a/lldb/source/Interpreter/OptionValueEnumeration.cpp b/lldb/source/Interpreter/OptionValueEnumeration.cpp
index cf64623..eb31bde 100644
--- a/lldb/source/Interpreter/OptionValueEnumeration.cpp
+++ b/lldb/source/Interpreter/OptionValueEnumeration.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueEnumeration.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/StringList.h"
using namespace lldb;
@@ -19,6 +20,17 @@ OptionValueEnumeration::OptionValueEnumeration(
SetEnumerations(enumerators);
}
+void OptionValueEnumeration::DumpEnum(Stream &strm, enum_type value) {
+ const size_t count = m_enumerations.GetSize();
+ for (size_t i = 0; i < count; ++i)
+ if (m_enumerations.GetValueAtIndexUnchecked(i).value == value) {
+ strm.PutCString(m_enumerations.GetCStringAtIndex(i));
+ return;
+ }
+
+ strm.Printf("%" PRIu64, (uint64_t)value);
+}
+
void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx,
Stream &strm, uint32_t dump_mask) {
if (dump_mask & eDumpOptionType)
@@ -26,14 +38,12 @@ void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx,
if (dump_mask & eDumpOptionValue) {
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
- const size_t count = m_enumerations.GetSize();
- for (size_t i = 0; i < count; ++i) {
- if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) {
- strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef());
- return;
- }
+ DumpEnum(strm, m_current_value);
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ DumpEnum(strm, m_default_value);
}
- strm.Printf("%" PRIu64, (uint64_t)m_current_value);
}
}
diff --git a/lldb/source/Interpreter/OptionValueFileSpec.cpp b/lldb/source/Interpreter/OptionValueFileSpec.cpp
index 6fa6af4..8d4966d 100644
--- a/lldb/source/Interpreter/OptionValueFileSpec.cpp
+++ b/lldb/source/Interpreter/OptionValueFileSpec.cpp
@@ -12,6 +12,7 @@
#include "lldb/Host/FileSystem.h"
#include "lldb/Interpreter/CommandCompletions.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/State.h"
@@ -41,7 +42,12 @@ void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx,
strm.PutCString(" = ");
if (m_current_value) {
- strm << '"' << m_current_value.GetPath().c_str() << '"';
+ strm << '"' << m_current_value.GetPath() << '"';
+ }
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value && m_default_value) {
+ DefaultValueFormat label(strm);
+ strm << '"' << m_default_value.GetPath() << '"';
}
}
}
diff --git a/lldb/source/Interpreter/OptionValueFileSpecList.cpp b/lldb/source/Interpreter/OptionValueFileSpecList.cpp
index f252dc4..f331c5d 100644
--- a/lldb/source/Interpreter/OptionValueFileSpecList.cpp
+++ b/lldb/source/Interpreter/OptionValueFileSpecList.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueFileSpecList.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/Stream.h"
@@ -22,9 +23,15 @@ void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx,
if (dump_mask & eDumpOptionValue) {
const bool one_line = dump_mask & eDumpOptionCommand;
const uint32_t size = m_current_value.GetSize();
- if (dump_mask & eDumpOptionType)
- strm.Printf(" =%s",
- (m_current_value.GetSize() > 0 && !one_line) ? "\n" : "");
+ if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) {
+ strm.Printf(" =");
+ if (dump_mask & eDumpOptionDefaultValue && !m_current_value.IsEmpty()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString("empty");
+ }
+ if (!m_current_value.IsEmpty() && !one_line)
+ strm.PutCString("\n");
+ }
if (!one_line)
strm.IndentMore();
for (uint32_t i = 0; i < size; ++i) {
diff --git a/lldb/source/Interpreter/OptionValueFormat.cpp b/lldb/source/Interpreter/OptionValueFormat.cpp
index bc4e779..05990fb 100644
--- a/lldb/source/Interpreter/OptionValueFormat.cpp
+++ b/lldb/source/Interpreter/OptionValueFormat.cpp
@@ -10,6 +10,7 @@
#include "lldb/DataFormatters/FormatManager.h"
#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
@@ -23,6 +24,11 @@ void OptionValueFormat::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
strm.PutCString(FormatManager::GetFormatAsCString(m_current_value));
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ strm.PutCString(FormatManager::GetFormatAsCString(m_default_value));
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueFormatEntity.cpp b/lldb/source/Interpreter/OptionValueFormatEntity.cpp
index d8b8301..b31dd4e 100644
--- a/lldb/source/Interpreter/OptionValueFormatEntity.cpp
+++ b/lldb/source/Interpreter/OptionValueFormatEntity.cpp
@@ -10,6 +10,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StringList.h"
using namespace lldb;
@@ -33,10 +34,9 @@ void OptionValueFormatEntity::Clear() {
m_value_was_set = false;
}
-static void EscapeBackticks(llvm::StringRef str, std::string &dst) {
- dst.clear();
+static std::string EscapeBackticks(llvm::StringRef str) {
+ std::string dst;
dst.reserve(str.size());
-
for (size_t i = 0, e = str.size(); i != e; ++i) {
char c = str[i];
if (c == '`') {
@@ -45,6 +45,7 @@ static void EscapeBackticks(llvm::StringRef str, std::string &dst) {
}
dst += c;
}
+ return dst;
}
void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx,
@@ -54,17 +55,18 @@ void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx,
if (dump_mask & eDumpOptionValue) {
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
- std::string escaped;
- EscapeBackticks(m_current_format, escaped);
- strm << '"' << escaped << '"';
+ strm << '"' << EscapeBackticks(m_current_format) << '"';
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_format != m_default_format) {
+ DefaultValueFormat label(strm);
+ strm << '"' << EscapeBackticks(m_default_format) << '"';
+ }
}
}
llvm::json::Value
OptionValueFormatEntity::ToJSON(const ExecutionContext *exe_ctx) const {
- std::string escaped;
- EscapeBackticks(m_current_format, escaped);
- return escaped;
+ return EscapeBackticks(m_current_format);
}
Status OptionValueFormatEntity::SetValueFromString(llvm::StringRef value_str,
diff --git a/lldb/source/Interpreter/OptionValueLanguage.cpp b/lldb/source/Interpreter/OptionValueLanguage.cpp
index 0fdaacb..bb28dc8 100644
--- a/lldb/source/Interpreter/OptionValueLanguage.cpp
+++ b/lldb/source/Interpreter/OptionValueLanguage.cpp
@@ -9,8 +9,9 @@
#include "lldb/Interpreter/OptionValueLanguage.h"
#include "lldb/DataFormatters/FormatManager.h"
-#include "lldb/Target/Language.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Language.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/Stream.h"
@@ -26,6 +27,12 @@ void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx,
strm.PutCString(" = ");
if (m_current_value != eLanguageTypeUnknown)
strm.PutCString(Language::GetNameForLanguageType(m_current_value));
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value &&
+ m_default_value != eLanguageTypeUnknown) {
+ DefaultValueFormat label(strm);
+ strm.PutCString(Language::GetNameForLanguageType(m_default_value));
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValuePathMappings.cpp b/lldb/source/Interpreter/OptionValuePathMappings.cpp
index 95b8e64..abf4d42 100644
--- a/lldb/source/Interpreter/OptionValuePathMappings.cpp
+++ b/lldb/source/Interpreter/OptionValuePathMappings.cpp
@@ -9,6 +9,7 @@
#include "lldb/Interpreter/OptionValuePathMappings.h"
#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/Stream.h"
@@ -28,8 +29,15 @@ void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx,
if (dump_mask & eDumpOptionType)
strm.Printf("(%s)", GetTypeAsCString());
if (dump_mask & eDumpOptionValue) {
- if (dump_mask & eDumpOptionType)
- strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : "");
+ if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) {
+ strm.Printf(" =");
+ if (dump_mask & eDumpOptionDefaultValue && !m_path_mappings.IsEmpty()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString("empty");
+ }
+ if (!m_path_mappings.IsEmpty())
+ strm.PutCString("\n");
+ }
m_path_mappings.Dump(&strm);
}
}
diff --git a/lldb/source/Interpreter/OptionValueRegex.cpp b/lldb/source/Interpreter/OptionValueRegex.cpp
index 91ec41d..30e307a 100644
--- a/lldb/source/Interpreter/OptionValueRegex.cpp
+++ b/lldb/source/Interpreter/OptionValueRegex.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueRegex.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
@@ -24,6 +25,12 @@ void OptionValueRegex::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
llvm::StringRef regex_text = m_regex.GetText();
strm.Printf("%s", regex_text.str().c_str());
}
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_regex.GetText() != m_default_regex_str &&
+ !m_default_regex_str.empty()) {
+ DefaultValueFormat label(strm);
+ strm.PutCString(m_default_regex_str);
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueSInt64.cpp b/lldb/source/Interpreter/OptionValueSInt64.cpp
index df7aee9..00f1cc3 100644
--- a/lldb/source/Interpreter/OptionValueSInt64.cpp
+++ b/lldb/source/Interpreter/OptionValueSInt64.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueSInt64.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
@@ -26,6 +27,11 @@ void OptionValueSInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
strm.Printf("%" PRIi64, m_current_value);
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ strm.Printf("%" PRIi64, m_default_value);
+ }
}
}
diff --git a/lldb/source/Interpreter/OptionValueString.cpp b/lldb/source/Interpreter/OptionValueString.cpp
index ae30661a..022a27d 100644
--- a/lldb/source/Interpreter/OptionValueString.cpp
+++ b/lldb/source/Interpreter/OptionValueString.cpp
@@ -9,12 +9,28 @@
#include "lldb/Interpreter/OptionValueString.h"
#include "lldb/Host/OptionParser.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
using namespace lldb_private;
+static void DumpString(Stream &strm, const std::string &str, bool escape,
+ bool raw) {
+ if (escape) {
+ std::string escaped_str;
+ Args::ExpandEscapedCharacters(str.c_str(), escaped_str);
+ DumpString(strm, escaped_str, false, raw);
+ return;
+ }
+
+ if (raw)
+ strm.PutCString(str);
+ else
+ strm.QuotedCString(str.c_str());
+}
+
void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
uint32_t dump_mask) {
if (dump_mask & eDumpOptionType)
@@ -22,21 +38,15 @@ void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionValue) {
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
- if (!m_current_value.empty() || m_value_was_set) {
- if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) {
- std::string expanded_escape_value;
- Args::ExpandEscapedCharacters(m_current_value.c_str(),
- expanded_escape_value);
- if (dump_mask & eDumpOptionRaw)
- strm.Printf("%s", expanded_escape_value.c_str());
- else
- strm.Printf("\"%s\"", expanded_escape_value.c_str());
- } else {
- if (dump_mask & eDumpOptionRaw)
- strm.Printf("%s", m_current_value.c_str());
- else
- strm.Printf("\"%s\"", m_current_value.c_str());
- }
+ const bool escape = m_options.Test(eOptionEncodeCharacterEscapeSequences);
+ const bool raw = dump_mask & eDumpOptionRaw;
+ if (!m_current_value.empty() || m_value_was_set)
+ DumpString(strm, m_current_value, escape, raw);
+
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value && !m_default_value.empty()) {
+ DefaultValueFormat label(strm);
+ DumpString(strm, m_default_value, escape, raw);
}
}
}
diff --git a/lldb/source/Interpreter/OptionValueUInt64.cpp b/lldb/source/Interpreter/OptionValueUInt64.cpp
index aa5e9a2..63f83cb 100644
--- a/lldb/source/Interpreter/OptionValueUInt64.cpp
+++ b/lldb/source/Interpreter/OptionValueUInt64.cpp
@@ -8,6 +8,7 @@
#include "lldb/Interpreter/OptionValueUInt64.h"
+#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Stream.h"
using namespace lldb;
@@ -30,6 +31,11 @@ void OptionValueUInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm,
if (dump_mask & eDumpOptionType)
strm.PutCString(" = ");
strm.Printf("%" PRIu64, m_current_value);
+ if (dump_mask & eDumpOptionDefaultValue &&
+ m_current_value != m_default_value) {
+ DefaultValueFormat label(strm);
+ strm.Printf("%" PRIu64, m_default_value);
+ }
}
}
diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp
index 4cf68db..ec72542 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();
@@ -278,7 +280,7 @@ void Options::OutputFormattedUsageText(Stream &strm,
if (static_cast<uint32_t>(actual_text.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.
@@ -312,7 +314,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 +388,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 +530,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: ");
diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
index fe9f5d0..1d210ea 100644
--- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
+++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp
@@ -1561,7 +1561,8 @@ void DynamicLoaderDarwinKernel::SetNotificationBreakpointIfNeeded() {
.CreateBreakpoint(&module_spec_list, nullptr,
"OSKextLoadedKextSummariesUpdated",
eFunctionNameTypeFull, eLanguageTypeUnknown, 0,
- skip_prologue, internal_bp, hardware)
+ /*offset_is_insn_count = */ false, skip_prologue,
+ internal_bp, hardware)
.get();
bp->SetCallback(DynamicLoaderDarwinKernel::BreakpointHitCallback, this,
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
index 08bef49..efb9ccc 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp
@@ -530,7 +530,7 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
m_process->GetTarget()
.CreateBreakpoint(&dyld_filelist, source_files,
"lldb_image_notifier", eFunctionNameTypeFull,
- eLanguageTypeUnknown, 0, skip_prologue,
+ eLanguageTypeUnknown, 0, false, skip_prologue,
internal, hardware)
.get();
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
@@ -546,8 +546,9 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() {
m_process->GetTarget()
.CreateBreakpoint(&dyld_filelist, source_files,
"gdb_image_notifier", eFunctionNameTypeFull,
- eLanguageTypeUnknown, 0, skip_prologue,
- internal, hardware)
+ eLanguageTypeUnknown, 0,
+ /*offset_is_insn_count = */ false,
+ skip_prologue, internal, hardware)
.get();
breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this,
true);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
index 2529e78..92094c0 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
@@ -123,6 +123,12 @@ private:
decl->setDeclContext(decl->getASTContext().getTranslationUnitDecl());
decl->setLexicalDeclContext(decl->getASTContext().getTranslationUnitDecl());
+ // Changing the DeclContext might change the linkage. For example, if the
+ // entity was previously declared inside a function, it will not be
+ // external, but changing the declaration context to the TU will make it
+ // external. Make sure this will recompute the linkage if it was computed
+ // before.
+ decl->invalidateCachedLinkage();
}
bool ChainPassesThrough(
@@ -229,6 +235,35 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener {
clang::ASTContext *m_src_ctx;
ClangASTImporter &importer;
+ void CompleteDecl(
+ Decl *decl,
+ lldb_private::ClangASTImporter::ASTContextMetadata const &to_context_md) {
+ // The decl that should be completed has to be imported into the target
+ // context from some other context.
+ assert(to_context_md.hasOrigin(decl));
+ // We should only complete decls coming from the source context.
+ assert(to_context_md.getOrigin(decl).ctx == m_src_ctx);
+
+ Decl *original_decl = to_context_md.getOrigin(decl).decl;
+
+ // Complete the decl now.
+ TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl);
+ if (auto *tag_decl = dyn_cast<TagDecl>(decl)) {
+ if (auto *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
+ if (original_tag_decl->isCompleteDefinition()) {
+ m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl);
+ tag_decl->setCompleteDefinition(true);
+ }
+ }
+
+ tag_decl->setHasExternalLexicalStorage(false);
+ tag_decl->setHasExternalVisibleStorage(false);
+ } else if (auto *container_decl = dyn_cast<ObjCContainerDecl>(decl)) {
+ container_decl->setHasExternalLexicalStorage(false);
+ container_decl->setHasExternalVisibleStorage(false);
+ }
+ }
+
public:
/// Constructs a CompleteTagDeclsScope.
/// \param importer The ClangASTImporter that we should observe.
@@ -251,30 +286,7 @@ public:
NamedDecl *decl = m_decls_to_complete.pop_back_val();
m_decls_already_completed.insert(decl);
- // The decl that should be completed has to be imported into the target
- // context from some other context.
- assert(to_context_md->hasOrigin(decl));
- // We should only complete decls coming from the source context.
- assert(to_context_md->getOrigin(decl).ctx == m_src_ctx);
-
- Decl *original_decl = to_context_md->getOrigin(decl).decl;
-
- // Complete the decl now.
- TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl);
- if (auto *tag_decl = dyn_cast<TagDecl>(decl)) {
- if (auto *original_tag_decl = dyn_cast<TagDecl>(original_decl)) {
- if (original_tag_decl->isCompleteDefinition()) {
- m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl);
- tag_decl->setCompleteDefinition(true);
- }
- }
-
- tag_decl->setHasExternalLexicalStorage(false);
- tag_decl->setHasExternalVisibleStorage(false);
- } else if (auto *container_decl = dyn_cast<ObjCContainerDecl>(decl)) {
- container_decl->setHasExternalLexicalStorage(false);
- container_decl->setHasExternalVisibleStorage(false);
- }
+ CompleteDecl(decl, *to_context_md);
to_context_md->removeOrigin(decl);
}
@@ -320,7 +332,8 @@ CompilerType ClangASTImporter::DeportType(TypeSystemClang &dst,
DeclContextOverride decl_context_override;
if (auto *t = ClangUtil::GetQualType(src_type)->getAs<TagType>())
- decl_context_override.OverrideAllDeclsFromContainingFunction(t->getDecl());
+ decl_context_override.OverrideAllDeclsFromContainingFunction(
+ t->getOriginalDecl());
CompleteTagDeclsScope complete_scope(*this, &dst.getASTContext(),
&src_ctxt->getASTContext());
@@ -358,6 +371,16 @@ clang::Decl *ClangASTImporter::DeportDecl(clang::ASTContext *dst_ctx,
return result;
}
+bool ClangASTImporter::CanImport(const Decl *d) {
+ if (!d)
+ return false;
+ if (isa<TagDecl>(d))
+ return GetDeclOrigin(d).Valid();
+ if (isa<ObjCInterfaceDecl>(d))
+ return GetDeclOrigin(d).Valid();
+ return false;
+}
+
bool ClangASTImporter::CanImport(const CompilerType &type) {
if (!ClangUtil::IsClangType(type))
return false;
@@ -367,24 +390,10 @@ bool ClangASTImporter::CanImport(const CompilerType &type) {
const clang::Type::TypeClass type_class = qual_type->getTypeClass();
switch (type_class) {
- case clang::Type::Record: {
- const clang::CXXRecordDecl *cxx_record_decl =
- qual_type->getAsCXXRecordDecl();
- if (cxx_record_decl) {
- if (GetDeclOrigin(cxx_record_decl).Valid())
- return true;
- }
- } break;
-
- case clang::Type::Enum: {
- clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getDecl();
- if (enum_decl) {
- if (GetDeclOrigin(enum_decl).Valid())
- return true;
- }
- } break;
-
+ case clang::Type::Record:
+ return CanImport(qual_type->getAsCXXRecordDecl());
+ case clang::Type::Enum:
+ return CanImport(llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl());
case clang::Type::ObjCObject:
case clang::Type::ObjCInterface: {
const clang::ObjCObjectType *objc_class_type =
@@ -394,10 +403,7 @@ bool ClangASTImporter::CanImport(const CompilerType &type) {
objc_class_type->getInterface();
// We currently can't complete objective C types through the newly added
// ASTContext because it only supports TagDecl objects right now...
- if (class_interface_decl) {
- if (GetDeclOrigin(class_interface_decl).Valid())
- return true;
- }
+ return CanImport(class_interface_decl);
}
} break;
@@ -414,12 +420,6 @@ bool ClangASTImporter::CanImport(const CompilerType &type) {
->getDeducedType()
.getAsOpaquePtr()));
- case clang::Type::Elaborated:
- return CanImport(CompilerType(type.GetTypeSystem(),
- llvm::cast<clang::ElaboratedType>(qual_type)
- ->getNamedType()
- .getAsOpaquePtr()));
-
case clang::Type::Paren:
return CanImport(CompilerType(
type.GetTypeSystem(),
@@ -452,7 +452,7 @@ bool ClangASTImporter::Import(const CompilerType &type) {
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
if (enum_decl) {
if (GetDeclOrigin(enum_decl).Valid())
return CompleteAndFetchChildren(qual_type);
@@ -488,12 +488,6 @@ bool ClangASTImporter::Import(const CompilerType &type) {
->getDeducedType()
.getAsOpaquePtr()));
- case clang::Type::Elaborated:
- return Import(CompilerType(type.GetTypeSystem(),
- llvm::cast<clang::ElaboratedType>(qual_type)
- ->getNamedType()
- .getAsOpaquePtr()));
-
case clang::Type::Paren:
return Import(CompilerType(
type.GetTypeSystem(),
@@ -597,7 +591,7 @@ bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
return false;
DeclFromUser<RecordDecl> origin_base_record(
- origin_base_record_type->getDecl());
+ origin_base_record_type->getOriginalDecl());
if (origin_base_record.IsInvalid())
return false;
@@ -728,7 +722,8 @@ bool ClangASTImporter::importRecordLayoutFromOrigin(
QualType base_type = bi->getType();
const RecordType *base_record_type = base_type->getAs<RecordType>();
- DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
+ DeclFromParser<RecordDecl> base_record(
+ base_record_type->getOriginalDecl());
DeclFromParser<CXXRecordDecl> base_cxx_record =
DynCast<CXXRecordDecl>(base_record);
@@ -860,7 +855,7 @@ bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) {
Log *log = GetLog(LLDBLog::Expressions);
if (const TagType *tag_type = type->getAs<TagType>()) {
- TagDecl *tag_decl = tag_type->getDecl();
+ TagDecl *tag_decl = tag_type->getOriginalDecl();
DeclOrigin decl_origin = GetDeclOrigin(tag_decl);
@@ -928,9 +923,9 @@ bool ClangASTImporter::RequireCompleteType(clang::QualType type) {
return false;
if (const TagType *tag_type = type->getAs<TagType>()) {
- TagDecl *tag_decl = tag_type->getDecl();
+ TagDecl *tag_decl = tag_type->getOriginalDecl();
- if (tag_decl->getDefinition() || tag_decl->isBeingDefined())
+ if (tag_decl->getDefinition())
return true;
return CompleteTagDecl(tag_decl);
@@ -1053,6 +1048,16 @@ ClangASTImporter::MapCompleter::~MapCompleter() = default;
llvm::Expected<Decl *>
ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) {
+ // FIXME: The Minimal import mode of clang::ASTImporter does not correctly
+ // import Lambda definitions. Work around this for now by not importing
+ // lambdas at all. This is most likely encountered when importing decls from
+ // the `std` module (not from debug-info), where lambdas can be defined in
+ // inline function bodies. Those will be imported by LLDB.
+ if (const auto *CXX = llvm::dyn_cast<clang::CXXRecordDecl>(From))
+ if (CXX->isLambda())
+ return llvm::make_error<ASTImportError>(
+ ASTImportError::UnsupportedConstruct);
+
if (m_std_handler) {
std::optional<Decl *> D = m_std_handler->Import(From);
if (D) {
@@ -1375,6 +1380,18 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
from, m_source_ctx, &to->getASTContext());
}
+ if (auto *to_namespace_decl = dyn_cast<NamespaceDecl>(to)) {
+ m_main.BuildNamespaceMap(to_namespace_decl);
+ to_namespace_decl->setHasExternalVisibleStorage();
+ }
+
+ MarkDeclImported(from, to);
+}
+
+void ClangASTImporter::ASTImporterDelegate::MarkDeclImported(Decl *from,
+ Decl *to) {
+ Log *log = GetLog(LLDBLog::Expressions);
+
if (auto *to_tag_decl = dyn_cast<TagDecl>(to)) {
to_tag_decl->setHasExternalLexicalStorage();
to_tag_decl->getPrimaryContext()->setMustBuildLookupTable();
@@ -1389,11 +1406,6 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from,
(to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete"));
}
- if (auto *to_namespace_decl = dyn_cast<NamespaceDecl>(to)) {
- m_main.BuildNamespaceMap(to_namespace_decl);
- to_namespace_decl->setHasExternalVisibleStorage();
- }
-
if (auto *to_container_decl = dyn_cast<ObjCContainerDecl>(to)) {
to_container_decl->setHasExternalLexicalStorage();
to_container_decl->setHasExternalVisibleStorage();
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
index 47b137a..03d2556 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h
@@ -157,6 +157,8 @@ public:
/// \see ClangASTImporter::Import
bool CanImport(const CompilerType &type);
+ bool CanImport(const clang::Decl *d);
+
/// If the given type was copied from another TypeSystemClang then copy over
/// all missing information (e.g., the definition of a 'class' type).
///
@@ -346,6 +348,8 @@ public:
llvm::Expected<clang::Decl *> ImportImpl(clang::Decl *From) override;
private:
+ void MarkDeclImported(clang::Decl *from, clang::Decl *to);
+
/// Decls we should ignore when mapping decls back to their original
/// ASTContext. Used by the CxxModuleHandler to mark declarations that
/// were created from the 'std' C++ module to prevent that the Importer
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
index 4b52f6a..21a9307 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
@@ -223,7 +223,7 @@ TagDecl *ClangASTSource::FindCompleteType(const TagDecl *decl) {
continue;
TagDecl *candidate_tag_decl =
- const_cast<TagDecl *>(tag_type->getDecl());
+ tag_type->getOriginalDecl()->getDefinitionOrSelf();
if (TypeSystemClang::GetCompleteDecl(
&candidate_tag_decl->getASTContext(), candidate_tag_decl))
@@ -250,7 +250,8 @@ TagDecl *ClangASTSource::FindCompleteType(const TagDecl *decl) {
if (!tag_type)
continue;
- TagDecl *candidate_tag_decl = const_cast<TagDecl *>(tag_type->getDecl());
+ TagDecl *candidate_tag_decl =
+ tag_type->getOriginalDecl()->getDefinitionOrSelf();
if (TypeSystemClang::GetCompleteDecl(&candidate_tag_decl->getASTContext(),
candidate_tag_decl))
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
index 214e260..8a68282 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -839,7 +839,7 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) {
clang::CXXRecordDecl *class_decl = method_decl->getParent();
- QualType class_qual_type(class_decl->getTypeForDecl(), 0);
+ QualType class_qual_type = m_ast_context->getCanonicalTagType(class_decl);
TypeFromUser class_user_type(
class_qual_type.getAsOpaquePtr(),
@@ -1561,7 +1561,7 @@ ClangExpressionDeclMap::AddExpressionVariable(NameSearchContext &context,
if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) {
if (const TagType *tag_type = dyn_cast<TagType>(parser_type))
- CompleteType(tag_type->getDecl());
+ CompleteType(tag_type->getOriginalDecl()->getDefinitionOrSelf());
if (const ObjCObjectPointerType *objc_object_ptr_type =
dyn_cast<ObjCObjectPointerType>(parser_type))
CompleteType(objc_object_ptr_type->getInterfaceDecl());
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
index aa0e6e3..319ff3fe8 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp
@@ -79,9 +79,11 @@ ClangPersistentVariables::GetCompilerTypeFromPersistentDecl(
if (p.m_decl == nullptr)
return std::nullopt;
+ auto ctx = std::static_pointer_cast<TypeSystemClang>(p.m_context.lock());
if (clang::TypeDecl *tdecl = llvm::dyn_cast<clang::TypeDecl>(p.m_decl)) {
- opaque_compiler_type_t t = static_cast<opaque_compiler_type_t>(
- const_cast<clang::Type *>(tdecl->getTypeForDecl()));
+ opaque_compiler_type_t t =
+ static_cast<opaque_compiler_type_t>(const_cast<clang::Type *>(
+ ctx->getASTContext().getTypeDeclType(tdecl).getTypePtr()));
return CompilerType(p.m_context, t);
}
return std::nullopt;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
index 45ad4f1..6f57c18 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
@@ -153,7 +153,7 @@ NameSearchContext::AddTypeDecl(const CompilerType &clang_type) {
return (NamedDecl *)typedef_name_decl;
} else if (const TagType *tag_type = qual_type->getAs<TagType>()) {
- TagDecl *tag_decl = tag_type->getDecl();
+ TagDecl *tag_decl = tag_type->getOriginalDecl()->getDefinitionOrSelf();
m_decls.push_back(tag_decl);
diff --git a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
index 29f03fee..a8901be 100644
--- a/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
+++ b/lldb/source/Plugins/Instruction/ARM64/EmulateInstructionARM64.cpp
@@ -404,7 +404,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
if (!success && !m_ignore_conditions)
return false;
- uint32_t orig_pc_value = 0;
+ uint64_t orig_pc_value = 0;
if (auto_advance_pc) {
orig_pc_value =
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
@@ -418,7 +418,7 @@ bool EmulateInstructionARM64::EvaluateInstruction(uint32_t evaluate_options) {
return false;
if (auto_advance_pc) {
- uint32_t new_pc_value =
+ uint64_t new_pc_value =
ReadRegisterUnsigned(eRegisterKindLLDB, gpr_pc_arm64, 0, &success);
if (!success)
return false;
diff --git a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp
index e67e60b..b89a6aa 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp
@@ -266,9 +266,9 @@ InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo(
// We gather symbolication addresses above, so no need for HistoryThread to
// try to infer the call addresses.
- bool pcs_are_call_addresses = true;
- ThreadSP new_thread_sp = std::make_shared<HistoryThread>(
- *process_sp, tid, PCs, pcs_are_call_addresses);
+ auto pc_type = HistoryPCType::Calls;
+ ThreadSP new_thread_sp =
+ std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type);
// Save this in the Process' ExtendedThreadList so a strong pointer retains
// the object
diff --git a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
index c2db354..565fd35 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp
@@ -324,9 +324,9 @@ InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo(
// We gather symbolication addresses above, so no need for HistoryThread to
// try to infer the call addresses.
- bool pcs_are_call_addresses = true;
- ThreadSP new_thread_sp = std::make_shared<HistoryThread>(
- *process_sp, tid, PCs, pcs_are_call_addresses);
+ auto pc_type = HistoryPCType::Calls;
+ ThreadSP new_thread_sp =
+ std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type);
std::string stop_reason_description = GetStopReasonDescription(info);
new_thread_sp->SetName(stop_reason_description.c_str());
diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
index 9648924..38c334b 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp
@@ -83,7 +83,7 @@ ReportRetriever::RetrieveReportData(const ProcessSP process_sp) {
options.SetAutoApplyFixIts(false);
options.SetLanguage(eLanguageTypeObjC_plus_plus);
- if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) {
+ if (auto [m, _] = GetPreferredAsanModule(process_sp->GetTarget()); m) {
SymbolContextList sc_list;
sc_list.Append(SymbolContext(std::move(m)));
options.SetPreferredSymbolContexts(std::move(sc_list));
diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp
index a5cee5d..09c6988 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp
+++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp
@@ -13,10 +13,12 @@
namespace lldb_private {
-lldb::ModuleSP GetPreferredAsanModule(const Target &target) {
- // Currently only supported on Darwin.
+std::tuple<lldb::ModuleSP, HistoryPCType>
+GetPreferredAsanModule(const Target &target) {
+ // Currently only Darwin provides ASan runtime support as part of the OS
+ // (libsanitizers).
if (!target.GetArchitecture().GetTriple().isOSDarwin())
- return nullptr;
+ return {nullptr, HistoryPCType::Calls};
lldb::ModuleSP module;
llvm::Regex pattern(R"(libclang_rt\.asan_.*_dynamic\.dylib)");
@@ -29,7 +31,16 @@ lldb::ModuleSP GetPreferredAsanModule(const Target &target) {
return IterationAction::Continue;
});
- return module;
+ // `Calls` - The ASan compiler-rt runtime already massages the return
+ // addresses into call addresses, so we don't want LLDB's unwinder to try to
+ // locate the previous instruction again as this might lead to us reporting
+ // a different line.
+ // `ReturnsNoZerothFrame` - Darwin, but not ASan compiler-rt implies
+ // libsanitizers which collects return addresses. It also discards a few
+ // non-user frames at the top of the stack.
+ auto pc_type =
+ (module ? HistoryPCType::Calls : HistoryPCType::ReturnsNoZerothFrame);
+ return {module, pc_type};
}
} // namespace lldb_private
diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h
index 425f0a2..e26d2bc 100644
--- a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h
+++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h
@@ -9,18 +9,20 @@
#ifndef LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H
#define LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H
+#include <tuple>
+
#include "lldb/lldb-forward.h"
+#include "lldb/lldb-private-enumerations.h"
namespace lldb_private {
-class Target;
-
/// On Darwin, if LLDB loaded libclang_rt, it's coming from a locally built
/// compiler-rt, and we should prefer it in favour of the system sanitizers
/// when running InstrumentationRuntime utility expressions that use symbols
/// from the sanitizer libraries. This helper searches the target for such a
/// dylib. Returns nullptr if no such dylib was found.
-lldb::ModuleSP GetPreferredAsanModule(const Target &target);
+std::tuple<lldb::ModuleSP, HistoryPCType>
+GetPreferredAsanModule(const Target &target);
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 3bc8708..c39b529 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -105,7 +105,9 @@ CPlusPlusLanguage::GetFunctionNameInfo(ConstString name) const {
bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const {
const char *mangled_name = mangled.GetMangledName().GetCString();
- return mangled_name && Mangled::IsMangledName(mangled_name);
+ auto mangling_scheme = Mangled::GetManglingScheme(mangled_name);
+ return mangled_name && (mangling_scheme == Mangled::eManglingSchemeItanium ||
+ mangling_scheme == Mangled::eManglingSchemeMSVC);
}
ConstString CPlusPlusLanguage::GetDemangledFunctionNameWithoutArguments(
@@ -392,13 +394,16 @@ GetDemangledScope(const SymbolContext &sc) {
return CPlusPlusLanguage::GetDemangledScope(demangled_name, info);
}
-/// Handles anything printed after the FunctionEncoding ItaniumDemangle
-/// node. Most notably the DotSuffix node.
-///
-/// FIXME: the suffix should also have an associated
-/// CPlusPlusLanguage::GetDemangledFunctionSuffix
-/// once we start setting the `DemangledNameInfo::SuffixRange`
-/// from inside the `TrackingOutputBuffer`.
+llvm::Expected<llvm::StringRef>
+CPlusPlusLanguage::GetDemangledFunctionSuffix(llvm::StringRef demangled,
+ const DemangledNameInfo &info) {
+ if (!info.hasSuffix())
+ return llvm::createStringError("Suffix range for '%s' is invalid.",
+ demangled.data());
+
+ return demangled.slice(info.SuffixRange.first, info.SuffixRange.second);
+}
+
static llvm::Expected<llvm::StringRef>
GetDemangledFunctionSuffix(const SymbolContext &sc) {
auto info_or_err = GetAndValidateInfo(sc);
@@ -407,11 +412,7 @@ GetDemangledFunctionSuffix(const SymbolContext &sc) {
auto [demangled_name, info] = *info_or_err;
- if (!info.hasSuffix())
- return llvm::createStringError("Suffix range for '%s' is invalid.",
- demangled_name.data());
-
- return demangled_name.slice(info.SuffixRange.first, info.SuffixRange.second);
+ return CPlusPlusLanguage::GetDemangledFunctionSuffix(demangled_name, info);
}
llvm::Expected<llvm::StringRef>
@@ -603,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;
@@ -750,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;
}
@@ -2424,7 +2325,7 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
return true;
}
case FormatEntity::Entry::Type::FunctionSuffix: {
- auto suffix_or_err = GetDemangledFunctionSuffix(sc);
+ auto suffix_or_err = ::GetDemangledFunctionSuffix(sc);
if (!suffix_or_err) {
LLDB_LOG_ERROR(
GetLog(LLDBLog::Language), suffix_or_err.takeError(),
@@ -2441,6 +2342,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 4f449f1..9a528ca 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -138,6 +138,10 @@ public:
GetDemangledFunctionArguments(llvm::StringRef demangled,
const DemangledNameInfo &info);
+ static llvm::Expected<llvm::StringRef>
+ GetDemangledFunctionSuffix(llvm::StringRef demangled,
+ const DemangledNameInfo &info);
+
// Extract C++ context and identifier from a string using heuristic matching
// (as opposed to
// CPlusPlusLanguage::CxxMethodName which has to have a fully qualified C++
@@ -160,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 ea1edbf..5289027 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 a787404..6053d04 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 d88a6ec..819f8a9 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 41441df..8576696 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 501fd09..f88a531 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 4bcdf01..60913e5 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -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/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
index 595e835..f4a695e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp
@@ -241,10 +241,11 @@ VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) {
bool lldb_private::formatters::LibStdcppStringSummaryProvider(
ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"});
- if (!ptr)
- return false;
+ if (!ptr || !ptr->GetError().Success())
+ stream << "Summary Unavailable";
+ else
+ stream << ptr->GetSummaryAsCString();
- stream << ptr->GetSummaryAsCString();
return true;
}
diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
index ef1c2c8..24e8489 100644
--- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
+++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp
@@ -73,7 +73,8 @@ static CompilerType GetLLDBNSPairType(TargetSP target_sp) {
static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair");
- compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(g_lldb_autogen_nspair);
+ compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(
+ scratch_ts_sp->getASTContext(), g_lldb_autogen_nspair);
if (!compiler_type) {
compiler_type = scratch_ts_sp->CreateRecordType(
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
index 24a7371..b1f2a66 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp
@@ -102,7 +102,7 @@ AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt,
resolver_sp = std::make_shared<BreakpointResolverName>(
bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
- eLazyBoolNo);
+ /*offset_is_insn_count = */ false, eLazyBoolNo);
// FIXME: don't do catch yet.
return resolver_sp;
}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
index cca721e..9beb133 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp
@@ -1163,7 +1163,7 @@ AppleObjCRuntimeV2::CreateExceptionResolver(const BreakpointSP &bkpt,
resolver_sp = std::make_shared<BreakpointResolverName>(
bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(),
eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0,
- eLazyBoolNo);
+ /*offset_is_insn_count = */ false, eLazyBoolNo);
// FIXME: We don't do catch breakpoints for ObjC yet.
// Should there be some way for the runtime to specify what it can do in this
// regard?
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
index a4b3e26..8dc5f51 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp
@@ -169,7 +169,8 @@ GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt,
if (throw_bp)
resolver_sp = std::make_shared<BreakpointResolverName>(
bkpt, "objc_exception_throw", eFunctionNameTypeBase,
- eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo);
+ eLanguageTypeUnknown, Breakpoint::Exact, 0,
+ /*offset_is_insn_count = */ false, eLazyBoolNo);
return resolver_sp;
}
diff --git a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
index afaaa57..206a471 100644
--- a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
+++ b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp
@@ -91,11 +91,9 @@ const char *memory_history_asan_command_format =
t;
)";
-static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
- ValueObjectSP return_value_sp,
- const char *type,
- const char *thread_name,
- HistoryThreads &result) {
+static void CreateHistoryThreadFromValueObject(
+ ProcessSP process_sp, ValueObjectSP return_value_sp, HistoryPCType pc_type,
+ const char *type, const char *thread_name, HistoryThreads &result) {
std::string count_path = "." + std::string(type) + "_count";
std::string tid_path = "." + std::string(type) + "_tid";
std::string trace_path = "." + std::string(type) + "_trace";
@@ -128,12 +126,8 @@ static void CreateHistoryThreadFromValueObject(ProcessSP process_sp,
pcs.push_back(pc);
}
- // The ASAN runtime already massages the return addresses into call
- // addresses, we don't want LLDB's unwinder to try to locate the previous
- // instruction again as this might lead to us reporting a different line.
- bool pcs_are_call_addresses = true;
HistoryThread *history_thread =
- new HistoryThread(*process_sp, tid, pcs, pcs_are_call_addresses);
+ new HistoryThread(*process_sp, tid, pcs, pc_type);
ThreadSP new_thread_sp(history_thread);
std::ostringstream thread_name_with_number;
thread_name_with_number << thread_name << " Thread " << tid;
@@ -176,7 +170,8 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
options.SetAutoApplyFixIts(false);
options.SetLanguage(eLanguageTypeObjC_plus_plus);
- if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) {
+ auto [m, pc_type] = GetPreferredAsanModule(process_sp->GetTarget());
+ if (m) {
SymbolContextList sc_list;
sc_list.Append(SymbolContext(std::move(m)));
options.SetPreferredSymbolContexts(std::move(sc_list));
@@ -197,10 +192,10 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) {
if (!return_value_sp)
return result;
- CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free",
- "Memory deallocated by", result);
- CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc",
- "Memory allocated by", result);
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,
+ "free", "Memory deallocated by", result);
+ CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type,
+ "alloc", "Memory allocated by", result);
return result;
}
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index f69358d..931baf5 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/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index 13df6e2..924e340 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -1156,6 +1156,7 @@ AddressClass ObjectFileMachO::GetAddressClass(lldb::addr_t file_addr) {
case eSectionTypeDataObjCMessageRefs:
case eSectionTypeDataObjCCFStrings:
case eSectionTypeGoSymtab:
+ case eSectionTypeWasmName:
return AddressClass::eData;
case eSectionTypeDebug:
@@ -2784,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;
@@ -3436,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;
}
@@ -3619,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;
@@ -3710,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;
@@ -4296,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;
}
@@ -4443,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);
@@ -4564,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
@@ -4592,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/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
index 25e9888..c361087 100644
--- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
+++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
@@ -308,40 +308,49 @@ Status MinidumpFileBuilder::AddModuleList() {
// the llvm::minidump::Module's structures into helper data
size_t size_before = GetCurrentDataEndOffset();
- // This is the size of the main part of the ModuleList stream.
- // It consists of a module number and corresponding number of
- // structs describing individual modules
- size_t module_stream_size =
- sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;
-
- // Adding directory describing this stream.
- error = AddDirectory(StreamType::ModuleList, module_stream_size);
- if (error.Fail())
- return error;
-
- m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));
-
// Temporary storage for the helper data (of variable length)
// as these cannot be dumped to m_data before dumping entire
// array of module structures.
DataBufferHeap helper_data;
+ // Vector to store modules that pass validation.
+ std::vector<std::pair<ModuleSP, uint64_t>> valid_modules;
+
for (size_t i = 0; i < modules_count; ++i) {
ModuleSP mod = modules.GetModuleAtIndex(i);
std::string module_name = mod->GetSpecificationDescription();
auto maybe_mod_size = getModuleFileSize(target, mod);
if (!maybe_mod_size) {
llvm::Error mod_size_err = maybe_mod_size.takeError();
- llvm::handleAllErrors(std::move(mod_size_err),
- [&](const llvm::ErrorInfoBase &E) {
- error = Status::FromErrorStringWithFormat(
- "Unable to get the size of module %s: %s.",
- module_name.c_str(), E.message().c_str());
- });
- return error;
+ Log *log = GetLog(LLDBLog::Object);
+ llvm::handleAllErrors(
+ std::move(mod_size_err), [&](const llvm::ErrorInfoBase &E) {
+ if (log) {
+ LLDB_LOGF(log, "Unable to get the size of module %s: %s",
+ module_name.c_str(), E.message().c_str());
+ }
+ });
+ continue;
}
+ valid_modules.emplace_back(mod, *maybe_mod_size);
+ }
+
+ size_t module_stream_size = sizeof(llvm::support::ulittle32_t) +
+ valid_modules.size() * minidump_module_size;
+
+ error = AddDirectory(StreamType::ModuleList, module_stream_size);
+ if (error.Fail())
+ return error;
- uint64_t mod_size = std::move(*maybe_mod_size);
+ // Setting the header with the number of modules.
+ llvm::support::ulittle32_t count =
+ static_cast<llvm::support::ulittle32_t>(valid_modules.size());
+ m_data.AppendData(&count, sizeof(llvm::support::ulittle32_t));
+
+ for (const auto &valid_module : valid_modules) {
+ ModuleSP mod = valid_module.first;
+ uint64_t module_size = valid_module.second;
+ std::string module_name = mod->GetSpecificationDescription();
llvm::support::ulittle32_t signature =
static_cast<llvm::support::ulittle32_t>(
@@ -381,7 +390,7 @@ Status MinidumpFileBuilder::AddModuleList() {
llvm::minidump::Module m{};
m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(
mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));
- m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);
+ m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(module_size);
m.Checksum = static_cast<llvm::support::ulittle32_t>(0);
m.TimeDateStamp =
static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
index b1efd25..492b441 100644
--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
+++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
@@ -22,6 +22,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/Support/CheckedArithmetic.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Format.h"
#include <optional>
@@ -35,6 +36,82 @@ LLDB_PLUGIN_DEFINE(ObjectFileWasm)
static const uint32_t kWasmHeaderSize =
sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion);
+/// Helper to read a 32-bit ULEB using LLDB's DataExtractor.
+static inline llvm::Expected<uint32_t> GetULEB32(DataExtractor &data,
+ lldb::offset_t &offset) {
+ const uint64_t value = data.GetULEB128(&offset);
+ if (value > std::numeric_limits<uint32_t>::max())
+ return llvm::createStringError("ULEB exceeds 32 bits");
+ return value;
+}
+
+/// Helper to read a 32-bit ULEB using LLVM's DataExtractor.
+static inline llvm::Expected<uint32_t>
+GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
+ const uint64_t value = data.getULEB128(c);
+ if (!c)
+ return c.takeError();
+ if (value > std::numeric_limits<uint32_t>::max())
+ return llvm::createStringError("ULEB exceeds 32 bits");
+ return value;
+}
+
+/// Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes.
+static inline llvm::Expected<std::string>
+GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
+ llvm::Expected<uint32_t> len = GetULEB32(data, c);
+ if (!len)
+ return len.takeError();
+
+ llvm::SmallVector<uint8_t, 32> str_storage;
+ data.getU8(c, str_storage, *len);
+ if (!c)
+ return c.takeError();
+
+ 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)
@@ -50,32 +127,6 @@ static bool ValidateModuleHeader(const DataBufferSP &data_sp) {
return version == llvm::wasm::WasmVersion;
}
-static std::optional<ConstString>
-GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) {
- // A Wasm string is encoded as a vector of UTF-8 codes.
- // Vectors are encoded with their u32 length followed by the element
- // sequence.
- uint64_t len = data.getULEB128(c);
- if (!c) {
- consumeError(c.takeError());
- return std::nullopt;
- }
-
- if (len >= (uint64_t(1) << 32)) {
- return std::nullopt;
- }
-
- llvm::SmallVector<uint8_t, 32> str_storage;
- data.getU8(c, str_storage, len);
- if (!c) {
- consumeError(c.takeError());
- return std::nullopt;
- }
-
- llvm::StringRef str = toStringRef(llvm::ArrayRef(str_storage));
- return ConstString(str);
-}
-
char ObjectFileWasm::ID;
void ObjectFileWasm::Initialize() {
@@ -174,7 +225,7 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {
if (!c)
return !llvm::errorToBool(c.takeError());
- if (payload_len >= (uint64_t(1) << 32))
+ if (payload_len > std::numeric_limits<uint32_t>::max())
return false;
if (section_id == llvm::wasm::WASM_SEC_CUSTOM) {
@@ -182,16 +233,19 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) {
// identifying the custom section, followed by an uninterpreted sequence
// of bytes.
lldb::offset_t prev_offset = c.tell();
- std::optional<ConstString> sect_name = GetWasmString(data, c);
- if (!sect_name)
+ llvm::Expected<std::string> sect_name = GetWasmString(data, c);
+ if (!sect_name) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Object), sect_name.takeError(),
+ "failed to parse section name: {0}");
return false;
+ }
if (payload_len < c.tell() - prev_offset)
return false;
uint32_t section_length = payload_len - (c.tell() - prev_offset);
m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length,
- section_id, *sect_name});
+ section_id, ConstString(*sect_name)});
*offset_ptr += (c.tell() + section_length);
} else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) {
m_sect_infos.push_back(section_info{*offset_ptr + c.tell(),
@@ -248,16 +302,221 @@ bool ObjectFileWasm::ParseHeader() {
return true;
}
-void ObjectFileWasm::ParseSymtab(Symtab &symtab) {}
+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<WasmFunction> functions;
+ functions.reserve(*function_count);
+
+ for (uint32_t i = 0; i < *function_count; ++i) {
+ llvm::Expected<uint32_t> function_size = GetULEB32(data, offset);
+ if (!function_size)
+ return function_size.takeError();
+ // 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.push_back({offset, *function_size});
+
+ std::optional<lldb::offset_t> next_offset =
+ llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size);
+ if (!next_offset)
+ return llvm::createStringError("function offset overflows 64 bits");
+ offset = *next_offset;
+ }
+
+ return functions;
+}
+
+struct WasmSegment {
+ enum SegmentType {
+ Active,
+ Passive,
+ };
+
+ std::string name;
+ 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;
+
+ 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);
+ if (!segment_count)
+ return segment_count.takeError();
+
+ std::vector<WasmSegment> segments;
+ segments.reserve(*segment_count);
+
+ for (uint32_t i = 0; i < *segment_count; ++i) {
+ llvm::Expected<uint32_t> flags = GetULEB32(data, offset);
+ 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 (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();
+
+ 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);
+ if (!next_offset)
+ return llvm::createStringError("segment offset overflows 64 bits");
+ offset = *next_offset;
+ }
+
+ return segments;
+}
+
+static llvm::Expected<std::vector<Symbol>>
+ParseNames(SectionSP code_section_sp, DataExtractor &name_data,
+ const std::vector<WasmFunction> &functions,
+ std::vector<WasmSegment> &segments) {
+
+ llvm::DataExtractor data = name_data.GetAsLLVM();
+ llvm::DataExtractor::Cursor c(0);
+ std::vector<Symbol> symbols;
+ while (c && c.tell() < data.size()) {
+ const uint8_t type = data.getU8(c);
+ llvm::Expected<uint32_t> size = GetULEB32(data, c);
+ if (!size)
+ return size.takeError();
+
+ switch (type) {
+ case llvm::wasm::WASM_NAMES_FUNCTION: {
+ const uint64_t count = data.getULEB128(c);
+ if (count > std::numeric_limits<uint32_t>::max())
+ return llvm::createStringError("function count overflows uint32_t");
+
+ for (uint64_t i = 0; c && i < count; ++i) {
+ llvm::Expected<uint32_t> idx = GetULEB32(data, c);
+ if (!idx)
+ return idx.takeError();
+ llvm::Expected<std::string> name = GetWasmString(data, c);
+ if (!name)
+ return name.takeError();
+ if (*idx >= functions.size())
+ continue;
+ symbols.emplace_back(
+ symbols.size(), *name, lldb::eSymbolTypeCode,
+ /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false,
+ /*is_artificial=*/false, code_section_sp,
+ functions[i].section_offset, functions[i].size,
+ /*size_is_valid=*/true, /*contains_linker_annotations=*/false,
+ /*flags=*/0);
+ }
+ } break;
+ case llvm::wasm::WASM_NAMES_DATA_SEGMENT: {
+ llvm::Expected<uint32_t> count = GetULEB32(data, c);
+ if (!count)
+ return count.takeError();
+ for (uint32_t i = 0; c && i < *count; ++i) {
+ llvm::Expected<uint32_t> idx = GetULEB32(data, c);
+ if (!idx)
+ return idx.takeError();
+ llvm::Expected<std::string> name = GetWasmString(data, c);
+ if (!name)
+ return name.takeError();
+ if (*idx >= segments.size())
+ continue;
+ // Update the segment name.
+ segments[i].name = *name;
+ }
+
+ } break;
+ case llvm::wasm::WASM_NAMES_GLOBAL:
+ case llvm::wasm::WASM_NAMES_LOCAL:
+ default:
+ std::optional<lldb::offset_t> offset =
+ llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size);
+ if (!offset)
+ return llvm::createStringError("offset overflows 64 bits");
+ c.seek(*offset);
+ }
+ }
+
+ if (!c)
+ return c.takeError();
+
+ return symbols;
+}
+
+void ObjectFileWasm::ParseSymtab(Symtab &symtab) {
+ for (const Symbol &symbol : m_symbols)
+ symtab.AddSymbol(symbol);
+
+ symtab.Finalize();
+ m_symbols.clear();
+}
static SectionType GetSectionTypeFromName(llvm::StringRef Name) {
- if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_")) {
+ if (Name == "name")
+ return lldb::eSectionTypeWasmName;
+ if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_"))
return ObjectFile::GetDWARFSectionTypeFromName(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;
@@ -271,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) {
@@ -294,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,
@@ -395,11 +738,15 @@ std::optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() {
const uint32_t kBufferSize = 1024;
DataExtractor section_header_data =
ReadImageData(sect_info.offset, kBufferSize);
+
llvm::DataExtractor data = section_header_data.GetAsLLVM();
llvm::DataExtractor::Cursor c(0);
- std::optional<ConstString> symbols_url = GetWasmString(data, c);
- if (symbols_url)
- return FileSpec(symbols_url->GetStringRef());
+ llvm::Expected<std::string> symbols_url = GetWasmString(data, c);
+ if (!symbols_url) {
+ llvm::consumeError(symbols_url.takeError());
+ return std::nullopt;
+ }
+ return FileSpec(*symbols_url);
}
}
return std::nullopt;
@@ -431,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 531b5f0..86ecbf2 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/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 1db7bc7..cd72454 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -1039,7 +1039,12 @@ ResolveSDKPathFromDebugInfo(lldb_private::Target *target) {
SymbolFile *sym_file = exe_module_sp->GetSymbolFile();
if (!sym_file)
- return llvm::createStringError("Failed to get symbol file from module");
+ return llvm::createStringError("Failed to get symbol file from executable");
+
+ if (sym_file->GetNumCompileUnits() == 0)
+ return llvm::createStringError(
+ "Failed to resolve SDK for target: executable's symbol file has no "
+ "compile units");
XcodeSDK merged_sdk;
for (unsigned i = 0; i < sym_file->GetNumCompileUnits(); ++i) {
@@ -1397,6 +1402,12 @@ PlatformDarwin::GetSDKPathFromDebugInfo(Module &module) {
llvm::formatv("No symbol file available for module '{0}'",
module.GetFileSpec().GetFilename().AsCString("")));
+ if (sym_file->GetNumCompileUnits() == 0)
+ return llvm::createStringError(
+ llvm::formatv("Could not resolve SDK for module '{0}'. Symbol file has "
+ "no compile units.",
+ module.GetFileSpec()));
+
bool found_public_sdk = false;
bool found_internal_sdk = false;
XcodeSDK merged_sdk;
diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt
index b0d1370..c35b4de 100644
--- a/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt
@@ -2,7 +2,7 @@ set(FBSDKERNEL_LIBS)
if(FBSDVMCore_FOUND)
list(APPEND FBSDKERNEL_LIBS fbsdvmcore)
endif()
-if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD")
list(APPEND FBSDKERNEL_LIBS kvm)
endif()
diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp
index fdafacf..c1bc6a3 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 3a31d68..cf36859 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 5d99c22..b1e326e 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/HistoryThread.cpp b/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
index bc06757..93efa2a 100644
--- a/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
+++ b/lldb/source/Plugins/Process/Utility/HistoryThread.cpp
@@ -27,13 +27,12 @@ using namespace lldb_private;
HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
std::vector<lldb::addr_t> pcs,
- bool pcs_are_call_addresses)
+ HistoryPCType pc_type)
: Thread(process, tid, true), m_framelist_mutex(), m_framelist(),
m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(),
m_thread_name(), m_originating_unique_thread_id(tid),
m_queue_id(LLDB_INVALID_QUEUE_ID) {
- m_unwinder_up =
- std::make_unique<HistoryUnwind>(*this, pcs, pcs_are_call_addresses);
+ m_unwinder_up = std::make_unique<HistoryUnwind>(*this, pcs, pc_type);
Log *log = GetLog(LLDBLog::Object);
LLDB_LOGF(log, "%p HistoryThread::HistoryThread", static_cast<void *>(this));
}
diff --git a/lldb/source/Plugins/Process/Utility/HistoryThread.h b/lldb/source/Plugins/Process/Utility/HistoryThread.h
index a66e0f2..cdc3b09 100644
--- a/lldb/source/Plugins/Process/Utility/HistoryThread.h
+++ b/lldb/source/Plugins/Process/Utility/HistoryThread.h
@@ -27,14 +27,14 @@ namespace lldb_private {
/// process execution
///
/// This subclass of Thread is used to provide a backtrace from earlier in
-/// process execution. It is given a backtrace list of pc addresses and it
-/// will create stack frames for them.
+/// process execution. It is given a backtrace list of pcs (return or call
+/// addresses) and it will create stack frames for them.
class HistoryThread : public lldb_private::Thread {
public:
HistoryThread(lldb_private::Process &process, lldb::tid_t tid,
std::vector<lldb::addr_t> pcs,
- bool pcs_are_call_addresses = false);
+ HistoryPCType pc_type = HistoryPCType::Returns);
~HistoryThread() override;
diff --git a/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp b/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
index 7749dc6..3b0618f 100644
--- a/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
+++ b/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp
@@ -24,9 +24,8 @@ using namespace lldb_private;
// Constructor
HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs,
- bool pcs_are_call_addresses)
- : Unwind(thread), m_pcs(pcs),
- m_pcs_are_call_addresses(pcs_are_call_addresses) {}
+ HistoryPCType pc_type)
+ : Unwind(thread), m_pcs(pcs), m_pc_type(pc_type) {}
// Destructor
@@ -52,6 +51,17 @@ HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) {
return rctx;
}
+static bool BehavesLikeZerothFrame(HistoryPCType pc_type, uint32_t frame_idx) {
+ switch (pc_type) {
+ case HistoryPCType::Returns:
+ return (frame_idx == 0);
+ case HistoryPCType::ReturnsNoZerothFrame:
+ return false;
+ case HistoryPCType::Calls:
+ return true;
+ }
+}
+
bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
lldb::addr_t &pc,
bool &behaves_like_zeroth_frame) {
@@ -61,10 +71,7 @@ bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
if (frame_idx < m_pcs.size()) {
cfa = frame_idx;
pc = m_pcs[frame_idx];
- if (m_pcs_are_call_addresses)
- behaves_like_zeroth_frame = true;
- else
- behaves_like_zeroth_frame = (frame_idx == 0);
+ behaves_like_zeroth_frame = BehavesLikeZerothFrame(m_pc_type, frame_idx);
return true;
}
return false;
diff --git a/lldb/source/Plugins/Process/Utility/HistoryUnwind.h b/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
index cb72b5d..3cf70a4 100644
--- a/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
+++ b/lldb/source/Plugins/Process/Utility/HistoryUnwind.h
@@ -19,7 +19,7 @@ namespace lldb_private {
class HistoryUnwind : public lldb_private::Unwind {
public:
HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs,
- bool pcs_are_call_addresses = false);
+ HistoryPCType pc_type = HistoryPCType::Returns);
~HistoryUnwind() override;
@@ -36,9 +36,7 @@ protected:
private:
std::vector<lldb::addr_t> m_pcs;
- /// This boolean indicates that the PCs in the non-0 frames are call
- /// addresses and not return addresses.
- bool m_pcs_are_call_addresses;
+ HistoryPCType m_pc_type;
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp
index 19601b7..f35027e 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 9b6ecd3..2dd11dc 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 0000000..803fed5
--- /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 0000000..253ae96
--- /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.h b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
index d2ddf7d..bca5bff 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
+++ b/lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h
@@ -45,8 +45,10 @@ public:
};
// based on RegisterContextDarwin_arm64.h
+ // Pack this so there are no extra bytes, but align its start address to at
+ // least 8 bytes to prevent alignment errors.
LLVM_PACKED_START
- struct GPR {
+ struct alignas(8) GPR {
uint64_t x[29]; // x0-x28
uint64_t fp; // x29
uint64_t lr; // x30
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 85e141d..91f3a6c 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/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index c9fe474..12cb257 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,37 +27,10 @@ using namespace llvm;
LLDB_PLUGIN_DEFINE(ProtocolServerMCP)
-static constexpr size_t kChunkSize = 1024;
-
-ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {
- AddRequestHandler("initialize",
- std::bind(&ProtocolServerMCP::InitializeHandler, this,
- std::placeholders::_1));
-
- AddRequestHandler("tools/list",
- std::bind(&ProtocolServerMCP::ToolsListHandler, this,
- std::placeholders::_1));
- AddRequestHandler("tools/call",
- std::bind(&ProtocolServerMCP::ToolsCallHandler, this,
- std::placeholders::_1));
-
- AddRequestHandler("resources/list",
- std::bind(&ProtocolServerMCP::ResourcesListHandler, this,
- std::placeholders::_1));
- AddRequestHandler("resources/read",
- std::bind(&ProtocolServerMCP::ResourcesReadHandler, this,
- std::placeholders::_1));
- 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."));
+static constexpr llvm::StringLiteral kName = "lldb-mcp";
+static constexpr llvm::StringLiteral kVersion = "0.1.0";
- AddResourceProvider(std::make_unique<DebuggerResourceProvider>());
-}
+ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {}
ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); }
@@ -66,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);
}
@@ -77,87 +53,41 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
return "MCP Server.";
}
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::Handle(lldb_protocol::mcp::Request request) {
- auto it = m_request_handlers.find(request.method);
- if (it != m_request_handlers.end()) {
- llvm::Expected<lldb_protocol::mcp::Response> response = it->second(request);
- if (!response)
- return response;
- response->id = request.id;
- return *response;
- }
-
- return make_error<MCPError>(
- llvm::formatv("no handler for request: {0}", request.method).str());
-}
-
-void ProtocolServerMCP::Handle(lldb_protocol::mcp::Notification notification) {
- auto it = m_notification_handlers.find(notification.method);
- if (it != m_notification_handlers.end()) {
- it->second(notification);
- return;
- }
-
- LLDB_LOG(GetLog(LLDBLog::Host), "MPC notification: {0} ({1})",
- notification.method, notification.params);
+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::MCPTransport>(
+ io_sp, io_sp, std::move(client_name), [&](llvm::StringRef message) {
+ LLDB_LOG(GetLog(LLDBLog::Host), "{0}", 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) {
- std::lock_guard<std::mutex> guard(m_server_mutex);
+ std::lock_guard<std::mutex> guard(m_mutex);
if (m_running)
return llvm::createStringError("the MCP server is already running");
@@ -177,6 +107,39 @@ 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(), ", ");
+
+ FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir();
+
+ Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath()));
+ if (error.Fail())
+ return error.takeError();
+
+ m_mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent(
+ formatv("lldb-mcp-{0}.json", getpid()).str());
+
+ ServerInfo info;
+ info.connection_uri = listening_uris[0];
+ info.pid = getpid();
+
+ std::string buf = formatv("{0}", toJSON(info)).str();
+ size_t num_bytes = buf.size();
+
+ const File::OpenOptions flags = File::eOpenOptionWriteOnly |
+ File::eOpenOptionCanCreate |
+ File::eOpenOptionTruncate;
+ llvm::Expected<lldb::FileUP> file =
+ FileSystem::Instance().Open(m_mcp_registry_entry_path, flags,
+ lldb::eFilePermissionsFileDefault, false);
+ if (!file)
+ return file.takeError();
+ if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError())
+ return error;
+
m_running = true;
m_listen_handlers = std::move(*handles);
m_loop_thread = std::thread([=] {
@@ -189,232 +152,23 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
llvm::Error ProtocolServerMCP::Stop() {
{
- std::lock_guard<std::mutex> guard(m_server_mutex);
+ std::lock_guard<std::mutex> guard(m_mutex);
if (!m_running)
return createStringError("the MCP sever is not running");
m_running = false;
}
+ if (!m_mcp_registry_entry_path.GetPath().empty())
+ FileSystem::Instance().RemoveFile(m_mcp_registry_entry_path);
+ m_mcp_registry_entry_path.Clear();
+
// 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_server_mutex);
- m_listener.reset();
- m_listen_handlers.clear();
- m_clients.clear();
- }
-
return llvm::Error::success();
}
-
-llvm::Expected<std::optional<lldb_protocol::mcp::Message>>
-ProtocolServerMCP::HandleData(llvm::StringRef data) {
- auto message = llvm::json::parse<lldb_protocol::mcp::Message>(/*JSON=*/data);
- if (!message)
- return message.takeError();
-
- if (const lldb_protocol::mcp::Request *request =
- std::get_if<lldb_protocol::mcp::Request>(&(*message))) {
- llvm::Expected<lldb_protocol::mcp::Response> response = Handle(*request);
-
- // Handle failures by converting them into an Error message.
- if (!response) {
- lldb_protocol::mcp::Error protocol_error;
- llvm::handleAllErrors(
- response.takeError(),
- [&](const MCPError &err) { protocol_error = err.toProtcolError(); },
- [&](const llvm::ErrorInfoBase &err) {
- protocol_error.error.code = MCPError::kInternalError;
- protocol_error.error.message = err.message();
- });
- protocol_error.id = request->id;
- return protocol_error;
- }
-
- return *response;
- }
-
- if (const lldb_protocol::mcp::Notification *notification =
- std::get_if<lldb_protocol::mcp::Notification>(&(*message))) {
- Handle(*notification);
- return std::nullopt;
- }
-
- if (std::get_if<lldb_protocol::mcp::Error>(&(*message)))
- return llvm::createStringError("unexpected MCP message: error");
-
- if (std::get_if<lldb_protocol::mcp::Response>(&(*message)))
- return llvm::createStringError("unexpected MCP message: response");
-
- llvm_unreachable("all message types handled");
-}
-
-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;
-}
-
-void ProtocolServerMCP::AddTool(std::unique_ptr<Tool> tool) {
- std::lock_guard<std::mutex> guard(m_server_mutex);
-
- if (!tool)
- return;
- m_tools[tool->GetName()] = std::move(tool);
-}
-
-void ProtocolServerMCP::AddResourceProvider(
- std::unique_ptr<ResourceProvider> resource_provider) {
- std::lock_guard<std::mutex> guard(m_server_mutex);
-
- if (!resource_provider)
- return;
- m_resource_providers.push_back(std::move(resource_provider));
-}
-
-void ProtocolServerMCP::AddRequestHandler(llvm::StringRef method,
- RequestHandler handler) {
- std::lock_guard<std::mutex> guard(m_server_mutex);
- m_request_handlers[method] = std::move(handler);
-}
-
-void ProtocolServerMCP::AddNotificationHandler(llvm::StringRef method,
- NotificationHandler handler) {
- std::lock_guard<std::mutex> guard(m_server_mutex);
- m_notification_handlers[method] = std::move(handler);
-}
-
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::InitializeHandler(
- const lldb_protocol::mcp::Request &request) {
- lldb_protocol::mcp::Response response;
- response.result.emplace(llvm::json::Object{
- {"protocolVersion", lldb_protocol::mcp::kVersion},
- {"capabilities", GetCapabilities()},
- {"serverInfo",
- llvm::json::Object{{"name", kName}, {"version", kVersion}}}});
- return response;
-}
-
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::ToolsListHandler(
- const lldb_protocol::mcp::Request &request) {
- lldb_protocol::mcp::Response response;
-
- llvm::json::Array tools;
- for (const auto &tool : m_tools)
- tools.emplace_back(toJSON(tool.second->GetDefinition()));
-
- response.result.emplace(llvm::json::Object{{"tools", std::move(tools)}});
-
- return response;
-}
-
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::ToolsCallHandler(
- const lldb_protocol::mcp::Request &request) {
- lldb_protocol::mcp::Response response;
-
- if (!request.params)
- return llvm::createStringError("no tool parameters");
-
- 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("");
- if (tool_name.empty())
- return llvm::createStringError("no tool name");
-
- auto it = m_tools.find(tool_name);
- if (it == m_tools.end())
- return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name));
-
- lldb_protocol::mcp::ToolArguments tool_args;
- if (const json::Value *args = param_obj->get("arguments"))
- tool_args = *args;
-
- llvm::Expected<lldb_protocol::mcp::TextResult> text_result =
- it->second->Call(tool_args);
- if (!text_result)
- return text_result.takeError();
-
- response.result.emplace(toJSON(*text_result));
-
- return response;
-}
-
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::ResourcesListHandler(
- const lldb_protocol::mcp::Request &request) {
- lldb_protocol::mcp::Response response;
-
- llvm::json::Array resources;
-
- std::lock_guard<std::mutex> guard(m_server_mutex);
- for (std::unique_ptr<ResourceProvider> &resource_provider_up :
- m_resource_providers) {
- for (const lldb_protocol::mcp::Resource &resource :
- resource_provider_up->GetResources())
- resources.push_back(resource);
- }
- response.result.emplace(
- llvm::json::Object{{"resources", std::move(resources)}});
-
- return response;
-}
-
-llvm::Expected<lldb_protocol::mcp::Response>
-ProtocolServerMCP::ResourcesReadHandler(
- const lldb_protocol::mcp::Request &request) {
- lldb_protocol::mcp::Response response;
-
- 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");
-
- llvm::StringRef uri_str = uri->getAsString().value_or("");
- if (uri_str.empty())
- return llvm::createStringError("no resource uri");
-
- std::lock_guard<std::mutex> guard(m_server_mutex);
- for (std::unique_ptr<ResourceProvider> &resource_provider_up :
- m_resource_providers) {
- llvm::Expected<lldb_protocol::mcp::ResourceResult> result =
- resource_provider_up->ReadResource(uri_str);
- if (result.errorIsA<UnsupportedURI>()) {
- llvm::consumeError(result.takeError());
- continue;
- }
- if (!result)
- return result.takeError();
-
- lldb_protocol::mcp::Response response;
- response.result.emplace(std::move(*result));
- return response;
- }
-
- return make_error<MCPError>(
- llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
- MCPError::kResourceNotFound);
-}
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index 2ea9585..004fa3c 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -13,9 +13,7 @@
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/Socket.h"
#include "lldb/Protocol/MCP/Protocol.h"
-#include "lldb/Protocol/MCP/Resource.h"
-#include "lldb/Protocol/MCP/Tool.h"
-#include "llvm/ADT/StringMap.h"
+#include "lldb/Protocol/MCP/Server.h"
#include <thread>
namespace lldb_private::mcp {
@@ -41,71 +39,24 @@ public:
Socket *GetSocket() const override { return m_listener.get(); }
protected:
- using RequestHandler =
- std::function<llvm::Expected<lldb_protocol::mcp::Response>(
- const lldb_protocol::mcp::Request &)>;
- using NotificationHandler =
- std::function<void(const lldb_protocol::mcp::Notification &)>;
-
- void AddTool(std::unique_ptr<lldb_protocol::mcp::Tool> tool);
- void AddResourceProvider(
- std::unique_ptr<lldb_protocol::mcp::ResourceProvider> resource_provider);
-
- void AddRequestHandler(llvm::StringRef method, RequestHandler handler);
- void AddNotificationHandler(llvm::StringRef method,
- NotificationHandler handler);
+ // 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);
- llvm::Expected<std::optional<lldb_protocol::mcp::Message>>
- HandleData(llvm::StringRef data);
-
- llvm::Expected<lldb_protocol::mcp::Response>
- Handle(lldb_protocol::mcp::Request request);
- void Handle(lldb_protocol::mcp::Notification notification);
-
- llvm::Expected<lldb_protocol::mcp::Response>
- InitializeHandler(const lldb_protocol::mcp::Request &);
-
- llvm::Expected<lldb_protocol::mcp::Response>
- ToolsListHandler(const lldb_protocol::mcp::Request &);
- llvm::Expected<lldb_protocol::mcp::Response>
- ToolsCallHandler(const lldb_protocol::mcp::Request &);
-
- llvm::Expected<lldb_protocol::mcp::Response>
- ResourcesListHandler(const lldb_protocol::mcp::Request &);
- llvm::Expected<lldb_protocol::mcp::Response>
- ResourcesReadHandler(const lldb_protocol::mcp::Request &);
-
- lldb_protocol::mcp::Capabilities GetCapabilities();
-
- llvm::StringLiteral kName = "lldb-mcp";
- llvm::StringLiteral kVersion = "0.1.0";
-
bool m_running = false;
- MainLoop m_loop;
+ FileSpec m_mcp_registry_entry_path;
+ 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::mutex m_server_mutex;
- llvm::StringMap<std::unique_ptr<lldb_protocol::mcp::Tool>> m_tools;
- std::vector<std::unique_ptr<lldb_protocol::mcp::ResourceProvider>>
- m_resource_providers;
-
- llvm::StringMap<RequestHandler> m_request_handlers;
- llvm::StringMap<NotificationHandler> m_notification_handlers;
+ 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 e94d2cd..5814245 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 e2382a7..0c65766 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 14347070..2f451bf 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 b7b1756..1886525 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/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
index f19dc8b..eb9e013 100644
--- a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
+++ b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp
@@ -47,7 +47,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType(
// See if we have made this type before and can reuse it.
CompilerType fields_type =
type_system->GetTypeForIdentifier<clang::CXXRecordDecl>(
- register_type_name);
+ type_system->getASTContext(), register_type_name);
if (!fields_type) {
// In most ABI, a change of field type means a change in storage unit.
@@ -83,7 +83,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType(
// may have built this one already.
CompilerType field_enum_type =
type_system->GetTypeForIdentifier<clang::EnumDecl>(
- enum_type_name);
+ type_system->getASTContext(), enum_type_name);
if (field_enum_type)
field_type = field_enum_type;
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
index 82aa022..a2a287a 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -71,24 +71,11 @@ Expected<std::string> python::As<std::string>(Expected<PythonObject> &&obj) {
return std::string(utf8.get());
}
-static bool python_is_finalizing() {
-#if PY_VERSION_HEX >= 0x030d0000
- return Py_IsFinalizing();
-#else
- return _Py_IsFinalizing();
-#endif
-}
-
void PythonObject::Reset() {
if (m_py_obj && Py_IsInitialized()) {
- if (python_is_finalizing()) {
- // Leak m_py_obj rather than crashing the process.
- // https://docs.python.org/3/c-api/init.html#c.PyGILState_Ensure
- } else {
- PyGILState_STATE state = PyGILState_Ensure();
- Py_DECREF(m_py_obj);
- PyGILState_Release(state);
- }
+ PyGILState_STATE state = PyGILState_Ensure();
+ Py_DECREF(m_py_obj);
+ PyGILState_Release(state);
}
m_py_obj = nullptr;
}
@@ -418,15 +405,33 @@ Expected<llvm::StringRef> PythonString::AsUTF8() const {
if (!IsValid())
return nullDeref();
- Py_ssize_t size;
- const char *data;
+ // PyUnicode_AsUTF8AndSize caches the UTF-8 representation of the string in
+ // the Unicode object, which makes it more efficient and ties the lifetime of
+ // the data to the Python string. However, it was only added to the Stable API
+ // in Python 3.10. Older versions that want to use the Stable API must use
+ // PyUnicode_AsUTF8String in combination with ConstString.
+#if defined(Py_LIMITED_API) && (Py_LIMITED_API < 0x030a0000)
+ PyObject *py_bytes = PyUnicode_AsUTF8String(m_py_obj);
+ if (!py_bytes)
+ return exception();
+ auto release_py_str =
+ llvm::make_scope_exit([py_bytes] { Py_DECREF(py_bytes); });
+ Py_ssize_t size = PyBytes_Size(py_bytes);
+ const char *str = PyBytes_AsString(py_bytes);
+
+ if (!str)
+ return exception();
- data = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
+ return ConstString(str, size).GetStringRef();
+#else
+ Py_ssize_t size;
+ const char *str = PyUnicode_AsUTF8AndSize(m_py_obj, &size);
- if (!data)
+ if (!str)
return exception();
- return llvm::StringRef(data, size);
+ return llvm::StringRef(str, size);
+#endif
}
size_t PythonString::GetSize() const {
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 24d604f..9330a63 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -92,10 +92,23 @@ namespace {
struct InitializePythonRAII {
public:
InitializePythonRAII() {
+ // The table of built-in modules can only be extended before Python is
+ // initialized.
+ if (!Py_IsInitialized()) {
+#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE
+ // Python's readline is incompatible with libedit being linked into lldb.
+ // Provide a patched version local to the embedded interpreter.
+ PyImport_AppendInittab("readline", initlldb_readline);
+#endif
+
+ // Register _lldb as a built-in module.
+ PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
+ }
+
+#if LLDB_EMBED_PYTHON_HOME
PyConfig config;
PyConfig_InitPythonConfig(&config);
-#if LLDB_EMBED_PYTHON_HOME
static std::string g_python_home = []() -> std::string {
if (llvm::sys::path::is_absolute(LLDB_PYTHON_HOME))
return LLDB_PYTHON_HOME;
@@ -109,34 +122,13 @@ public:
if (!g_python_home.empty()) {
PyConfig_SetBytesString(&config, &config.home, g_python_home.c_str());
}
-#endif
-
- // The table of built-in modules can only be extended before Python is
- // initialized.
- if (!Py_IsInitialized()) {
-#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE
- // Python's readline is incompatible with libedit being linked into lldb.
- // Provide a patched version local to the embedded interpreter.
- bool ReadlinePatched = false;
- for (auto *p = PyImport_Inittab; p->name != nullptr; p++) {
- if (strcmp(p->name, "readline") == 0) {
- p->initfunc = initlldb_readline;
- break;
- }
- }
- if (!ReadlinePatched) {
- PyImport_AppendInittab("readline", initlldb_readline);
- ReadlinePatched = true;
- }
-#endif
-
- // Register _lldb as a built-in module.
- PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
- }
config.install_signal_handlers = 0;
Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
+#else
+ Py_InitializeEx(/*install_sigs=*/0);
+#endif
// The only case we should go further and acquire the GIL: it is unlocked.
PyGILState_STATE gil_state = PyGILState_Ensure();
@@ -907,11 +899,11 @@ bool ScriptInterpreterPythonImpl::Interrupt() {
Log *log = GetLog(LLDBLog::Script);
if (IsExecutingPython()) {
- PyThreadState *state = PyThreadState_GET();
+ PyThreadState *state = PyThreadState_Get();
if (!state)
state = GetThreadState();
if (state) {
- long tid = state->thread_id;
+ long tid = PyThread_get_thread_ident();
PyThreadState_Swap(state);
int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
LLDB_LOGF(log,
diff --git a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
index 867f6a6..70093c9 100644
--- a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
+++ b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
@@ -1601,6 +1601,7 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
const char *func_name = "_libtrace_init";
const lldb::addr_t offset = 0;
+ const bool offset_is_insn_count = false;
const LazyBool skip_prologue = eLazyBoolCalculate;
// This is an internal breakpoint - the user shouldn't see it.
const bool internal = true;
@@ -1608,7 +1609,8 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) {
auto breakpoint_sp = target.CreateBreakpoint(
&module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull,
- eLanguageTypeC, offset, skip_prologue, internal, hardware);
+ eLanguageTypeC, offset, offset_is_insn_count, skip_prologue, internal,
+ hardware);
if (!breakpoint_sp) {
// Huh? Bail here.
LLDB_LOGF(log,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
index a9345c7..1bfa17f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp
@@ -44,7 +44,7 @@ class ElaboratingDIEIterator
// Container sizes are optimized for the case of following DW_AT_specification
// and DW_AT_abstract_origin just once.
llvm::SmallVector<DWARFDIE, 2> m_worklist;
- llvm::SmallSet<DWARFDebugInfoEntry *, 3> m_seen;
+ llvm::SmallPtrSet<DWARFDebugInfoEntry *, 3> m_seen;
void Next() {
assert(!m_worklist.empty() && "Incrementing end iterator?");
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
index f968eee..849f573 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -284,9 +284,10 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
/// Adds all attributes of the DIE at the top of the \c worklist to the
/// \c attributes list. Specifcations and abstract origins are added
/// to the \c worklist if the referenced DIE has not been seen before.
-static bool GetAttributes(llvm::SmallVectorImpl<DWARFDIE> &worklist,
- llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen,
- DWARFAttributes &attributes) {
+static bool
+GetAttributes(llvm::SmallVectorImpl<DWARFDIE> &worklist,
+ llvm::SmallPtrSet<DWARFDebugInfoEntry const *, 3> &seen,
+ DWARFAttributes &attributes) {
assert(!worklist.empty() && "Need at least one DIE to visit.");
assert(seen.size() >= 1 &&
"Need to have seen at least the currently visited entry.");
@@ -366,7 +367,7 @@ DWARFAttributes DWARFDebugInfoEntry::GetAttributes(const DWARFUnit *cu,
// Keep track if DIEs already seen to prevent infinite recursion.
// Value of '3' was picked for the same reason that
// DWARFDie::findRecursively does.
- llvm::SmallSet<DWARFDebugInfoEntry const *, 3> seen;
+ llvm::SmallPtrSet<DWARFDebugInfoEntry const *, 3> seen;
seen.insert(this);
do {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 84b3da3..b15e0c1 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -8,6 +8,7 @@
#include "SymbolFileDWARF.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/Support/Casting.h"
@@ -998,12 +999,12 @@ XcodeSDK SymbolFileDWARF::ParseXcodeSDK(CompileUnit &comp_unit) {
const char *sdk = cu_die.GetAttributeValueAsString(DW_AT_APPLE_sdk, nullptr);
if (!sdk)
return {};
- std::string sysroot =
+ llvm::StringRef sysroot =
cu_die.GetAttributeValueAsString(DW_AT_LLVM_sysroot, "");
// RegisterXcodeSDK calls into xcrun which is not aware of CLT, which is
// expensive.
- if (sysroot.find("/Library/Developer/CommandLineTools/SDKs") != 0) {
+ if (!sysroot.starts_with("/Library/Developer/CommandLineTools/SDKs")) {
// Register the sysroot path remapping with the module belonging to
// the CU as well as the one belonging to the symbol file. The two
// would be different if this is an OSO object and module is the
@@ -1017,7 +1018,7 @@ XcodeSDK SymbolFileDWARF::ParseXcodeSDK(CompileUnit &comp_unit) {
local_module_sp->RegisterXcodeSDK(sdk, sysroot);
}
- return {sdk, FileSpec{std::move(sysroot)}};
+ return {sdk, FileSpec(sysroot)};
}
size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) {
@@ -2483,6 +2484,30 @@ 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;
+
+ // 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;
+ });
+
+ return definition;
+}
+
llvm::Expected<SymbolContext>
SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
@@ -2495,37 +2520,19 @@ 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)) {
- 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;
-
- // 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.
- die = entry;
- return IterationAction::Stop;
- });
+ auto definition = FindFunctionDefinition(label);
+ if (!definition)
+ return llvm::createStringError("failed to find definition DIE");
- if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
- return llvm::createStringError(
- llvm::formatv("failed to find definition DIE for {0}", label));
+ die = std::move(definition);
}
SymbolContextList sc_list;
if (!ResolveFunction(die, /*include_inlines=*/false, sc_list))
- return llvm::createStringError(
- llvm::formatv("failed to resolve function for {0}", label));
+ return llvm::createStringError("failed to resolve function");
if (sc_list.IsEmpty())
- return llvm::createStringError(
- llvm::formatv("failed to find function for {0}", label));
+ return llvm::createStringError("failed to find function");
assert(sc_list.GetSize() == 1);
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 5042d91..d7db8a3 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -373,6 +373,13 @@ public:
/// Returns the DWARFIndex for this symbol, if it exists.
DWARFIndex *getIndex() { return m_index.get(); }
+private:
+ /// Find the definition DIE for the specified \c label in this
+ /// SymbolFile.
+ ///
+ /// \returns A valid definition DIE on success.
+ DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label);
+
protected:
SymbolFileDWARF(const SymbolFileDWARF &) = delete;
const SymbolFileDWARF &operator=(const SymbolFileDWARF &) = delete;
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
index f01fba3..709281c 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -449,7 +449,7 @@ bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) {
->GetIndex();
lldbassert(IsTagRecord(type_id, index.tpi()));
- clang::QualType tag_qt = m_clang.getASTContext().getTypeDeclType(&tag);
+ clang::QualType tag_qt = m_clang.getASTContext().getCanonicalTagType(&tag);
TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false);
TypeIndex tag_ti = type_id.index;
@@ -562,7 +562,8 @@ clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) {
m_clang.getASTContext(), spelling));
}
return m_clang.getASTContext().getMemberPointerType(
- pointee_type, /*Qualifier=*/nullptr, class_type->getAsCXXRecordDecl());
+ pointee_type, /*Qualifier=*/std::nullopt,
+ class_type->getAsCXXRecordDecl());
}
clang::QualType pointer_type;
@@ -862,9 +863,9 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id,
SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>(
m_clang.GetSymbolFile()->GetBackingSymbolFile());
PdbIndex &index = pdb->GetIndex();
- clang::QualType parent_qt = llvm::cast<clang::TypeDecl>(parent)
- ->getTypeForDecl()
- ->getCanonicalTypeInternal();
+ clang::CanQualType parent_qt =
+ m_clang.getASTContext().getCanonicalTypeDeclType(
+ llvm::cast<clang::TypeDecl>(parent));
lldb::opaque_compiler_type_t parent_opaque_ty =
ToCompilerType(parent_qt).GetOpaqueQualType();
// FIXME: Remove this workaround.
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index dcea33d..112eb06 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -39,6 +39,7 @@
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/DebugInfo/PDB/PDB.h"
@@ -643,8 +644,14 @@ SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id,
std::string uname = GetUnqualifiedTypeName(record);
- // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
+ llvm::Expected<Declaration> maybeDecl = ResolveUdtDeclaration(type_id);
Declaration decl;
+ if (maybeDecl)
+ decl = std::move(*maybeDecl);
+ else
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(),
+ "Failed to resolve declaration for '{1}': {0}", uname);
+
return MakeType(toOpaqueUid(type_id), ConstString(uname), size, nullptr,
LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct,
Type::ResolveState::Forward);
@@ -667,7 +674,14 @@ lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
CompilerType ct) {
std::string uname = GetUnqualifiedTypeName(er);
+ llvm::Expected<Declaration> maybeDecl = ResolveUdtDeclaration(type_id);
Declaration decl;
+ if (maybeDecl)
+ decl = std::move(*maybeDecl);
+ else
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(),
+ "Failed to resolve declaration for '{1}': {0}", uname);
+
TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
return MakeType(
@@ -1641,6 +1655,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) {
clang->GetNativePDBParser()->Dump(s, filter);
}
+void SymbolFileNativePDB::CacheFunctionNames() {
+ if (!m_func_full_names.IsEmpty())
+ return;
+
+ // (segment, code offset) -> gid
+ std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids;
+
+ // First, find all function references 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();
+ if (kind != S_PROCREF && kind != S_LPROCREF)
+ continue;
+
+ ProcRefSym ref =
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym));
+ if (ref.Name.empty())
+ continue;
+
+ // Find the function this is referencing.
+ CompilandIndexItem &cci =
+ m_index->compilands().GetOrCreateCompiland(ref.modi());
+ auto iter = cci.m_debug_stream.getSymbolArray().at(ref.SymOffset);
+ if (iter == cci.m_debug_stream.getSymbolArray().end())
+ continue;
+ kind = iter->kind();
+ if (kind != S_GPROC32 && kind != S_LPROC32)
+ continue;
+
+ ProcSym proc =
+ llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(*iter));
+ if ((proc.Flags & ProcSymFlags::IsUnreachable) != ProcSymFlags::None)
+ continue;
+ if (proc.Name.empty() || proc.FunctionType.isSimple())
+ continue;
+
+ // 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);
+
+ llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name);
+ if (basename.empty())
+ basename = proc.Name;
+
+ m_func_base_names.Append(ConstString(basename), gid);
+ m_func_full_names.Append(ConstString(proc.Name), gid);
+
+ // To see if this is a member function, check the type.
+ auto type = m_index->tpi().getType(proc.FunctionType);
+ if (type.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfr;
+ llvm::cantFail(
+ TypeDeserializer::deserializeAs<MemberFunctionRecord>(type, mfr));
+ if (!mfr.getThisType().isNoneType())
+ m_func_method_names.Append(ConstString(basename), gid);
+ }
+ }
+
+ // The publics stream contains all mangled function names and their address.
+ for (auto pid : m_index->publics().getPublicsTable()) {
+ PdbGlobalSymId global{pid, true};
+ CVSymbol sym = m_index->ReadSymbolRecord(global);
+ auto kind = sym.kind();
+ if (kind != S_PUB32)
+ continue;
+ PublicSym32 pub =
+ llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym));
+ // We only care about mangled names - if the name isn't mangled, it's
+ // already in the full name map.
+ if (!Mangled::IsMangledName(pub.Name))
+ 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())
+ 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.SizeToFit();
+ m_func_method_names.Sort();
+ m_func_method_names.SizeToFit();
+ m_func_base_names.Sort();
+ m_func_base_names.SizeToFit();
+}
+
void SymbolFileNativePDB::FindGlobalVariables(
ConstString name, const CompilerDeclContext &parent_decl_ctx,
uint32_t max_matches, VariableList &variables) {
@@ -1677,34 +1779,60 @@ void SymbolFileNativePDB::FindFunctions(
if (name_type_mask & eFunctionNameTypeFull)
name = lookup_info.GetName();
- // For now we only support lookup by method name or full name.
if (!(name_type_mask & eFunctionNameTypeFull ||
+ name_type_mask & eFunctionNameTypeBase ||
name_type_mask & eFunctionNameTypeMethod))
return;
+ CacheFunctionNames();
- using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+ std::set<uint32_t> resolved_ids; // avoid duplicate lookups
+ auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) {
+ std::vector<uint32_t> ids;
+ if (!Names.GetValues(name, ids))
+ return;
- std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
- name.GetStringRef(), m_index->symrecords());
- for (const SymbolAndOffset &match : matches) {
- if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
- continue;
- ProcRefSym proc(match.second.kind());
- cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
+ for (uint32_t id : ids) {
+ if (!resolved_ids.insert(id).second)
+ continue;
- if (!IsValidRecord(proc))
- continue;
+ PdbGlobalSymId global{id, false};
+ if (parent_decl_ctx.IsValid() &&
+ GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx)
+ continue;
- CompilandIndexItem &cci =
- m_index->compilands().GetOrCreateCompiland(proc.modi());
- SymbolContext sc;
+ CVSymbol sym = m_index->ReadSymbolRecord(global);
+ auto kind = sym.kind();
+ lldbassert(kind == S_PROCREF || kind == S_LPROCREF);
- sc.comp_unit = GetOrCreateCompileUnit(cci).get();
- PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
- sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+ ProcRefSym proc =
+ cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym));
- sc_list.Append(sc);
- }
+ if (!IsValidRecord(proc))
+ continue;
+
+ CompilandIndexItem &cci =
+ m_index->compilands().GetOrCreateCompiland(proc.modi());
+ SymbolContext sc;
+
+ sc.comp_unit = GetOrCreateCompileUnit(cci).get();
+ if (!sc.comp_unit)
+ continue;
+
+ PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
+ sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+ if (!sc.function)
+ continue;
+
+ sc_list.Append(sc);
+ }
+ };
+
+ if (name_type_mask & eFunctionNameTypeFull)
+ resolve_from(m_func_full_names);
+ if (name_type_mask & eFunctionNameTypeBase)
+ resolve_from(m_func_base_names);
+ if (name_type_mask & eFunctionNameTypeMethod)
+ resolve_from(m_func_method_names);
}
void SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,
@@ -2441,3 +2569,70 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
}
return ctx;
}
+
+void SymbolFileNativePDB::CacheUdtDeclarations() {
+ for (CVType cvt : m_index->ipi().typeArray()) {
+ switch (cvt.kind()) {
+ case LF_UDT_SRC_LINE: {
+ UdtSourceLineRecord udt_src;
+ llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_src));
+ m_udt_declarations.try_emplace(
+ udt_src.UDT, UdtDeclaration{/*FileNameIndex=*/udt_src.SourceFile,
+ /*IsIpiIndex=*/true,
+ /*Line=*/udt_src.LineNumber});
+ } break;
+ case LF_UDT_MOD_SRC_LINE: {
+ UdtModSourceLineRecord udt_mod_src;
+ llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_mod_src));
+ // Some types might be contributed by multiple modules. We assume that
+ // they all point to the same file and line because we can only provide
+ // one location.
+ m_udt_declarations.try_emplace(
+ udt_mod_src.UDT,
+ UdtDeclaration{/*FileNameIndex=*/udt_mod_src.SourceFile,
+ /*IsIpiIndex=*/false,
+ /*Line=*/udt_mod_src.LineNumber});
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
+llvm::Expected<Declaration>
+SymbolFileNativePDB::ResolveUdtDeclaration(PdbTypeSymId type_id) {
+ std::call_once(m_cached_udt_declarations, [this] { CacheUdtDeclarations(); });
+
+ auto it = m_udt_declarations.find(type_id.index);
+ if (it == m_udt_declarations.end())
+ return llvm::createStringError("No UDT declaration found");
+
+ llvm::StringRef file_name;
+ if (it->second.IsIpiIndex) {
+ CVType cvt = m_index->ipi().getType(it->second.FileNameIndex);
+ if (cvt.kind() != LF_STRING_ID)
+ return llvm::createStringError("File name was not a LF_STRING_ID");
+
+ StringIdRecord sid;
+ llvm::cantFail(TypeDeserializer::deserializeAs(cvt, sid));
+ file_name = sid.String;
+ } else {
+ // The file name index is an index into the string table
+ auto string_table = m_index->pdb().getStringTable();
+ if (!string_table)
+ return string_table.takeError();
+
+ llvm::Expected<llvm::StringRef> string =
+ string_table->getStringTable().getString(
+ it->second.FileNameIndex.getIndex());
+ if (!string)
+ return string.takeError();
+ file_name = *string;
+ }
+
+ // rustc sets the filename to "<unknown>" for some files
+ if (file_name == "\\<unknown>")
+ return Declaration();
+
+ return Declaration(FileSpec(file_name), it->second.Line);
+}
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index eda375d..cfa0041 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -260,6 +260,11 @@ private:
std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
+ void CacheFunctionNames();
+
+ void CacheUdtDeclarations();
+ llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id);
+
llvm::BumpPtrAllocator m_allocator;
lldb::addr_t m_obj_load_address = 0;
@@ -281,7 +286,26 @@ private:
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
m_parent_types;
+ struct UdtDeclaration {
+ /// This could either be an index into the `/names` section (string table,
+ /// LF_UDT_MOD_SRC_LINE) or, this could be an index into the IPI stream to a
+ /// LF_STRING_ID record (LF_UDT_SRC_LINE).
+ llvm::codeview::TypeIndex FileNameIndex;
+ bool IsIpiIndex;
+
+ uint32_t Line;
+ };
+ llvm::DenseMap<llvm::codeview::TypeIndex, UdtDeclaration> m_udt_declarations;
+ std::once_flag m_cached_udt_declarations;
+
lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
+
+ /// mangled name/full function name -> Global ID(s)
+ lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
+ /// basename -> Global ID(s)
+ lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
+ /// method basename -> Global ID(s)
+ lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
};
} // namespace npdb
diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
index 8b8eac6e..3a95588 100644
--- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
+++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp
@@ -407,8 +407,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) {
// symbols in PDB for types with const or volatile modifiers, but we need
// to create only one declaration for them all.
Type::ResolveState type_resolve_state;
- CompilerType clang_type =
- m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(name, decl_context);
+ CompilerType clang_type = m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(
+ m_ast.getASTContext(), name, decl_context);
if (!clang_type.IsValid()) {
auto access = GetAccessibilityForUdt(*udt);
@@ -479,8 +479,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) {
uint64_t bytes = enum_type->getLength();
// Check if such an enum already exists in the current context
- CompilerType ast_enum =
- m_ast.GetTypeForIdentifier<clang::EnumDecl>(name, decl_context);
+ CompilerType ast_enum = m_ast.GetTypeForIdentifier<clang::EnumDecl>(
+ m_ast.getASTContext(), name, decl_context);
if (!ast_enum.IsValid()) {
auto underlying_type_up = enum_type->getUnderlyingType();
if (!underlying_type_up)
@@ -557,7 +557,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) {
// Check if such a typedef already exists in the current context
CompilerType ast_typedef =
- m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(name, decl_ctx);
+ m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(
+ m_ast.getASTContext(), name, decl_ctx);
if (!ast_typedef.IsValid()) {
CompilerType target_ast_type = target_type->GetFullCompilerType();
diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp
index b23f642..04a25e4 100644
--- a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp
+++ b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp
@@ -544,9 +544,9 @@ ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceThread(ThreadSP real_thread,
if (!thread_extended_info->ForEach(extract_frame_pc))
return {};
- originating_thread_sp =
- std::make_shared<HistoryThread>(*m_process, real_thread->GetIndexID(),
- app_specific_backtrace_pcs, true);
+ originating_thread_sp = std::make_shared<HistoryThread>(
+ *m_process, real_thread->GetIndexID(), app_specific_backtrace_pcs,
+ HistoryPCType::Calls);
originating_thread_sp->SetQueueName(type.AsCString());
}
return originating_thread_sp;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 6addf4f..39aacdb 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -27,6 +27,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Mangle.h"
+#include "clang/AST/QualTypeNames.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
#include "clang/AST/VTableBuilder.h"
@@ -161,8 +162,7 @@ void addOverridesForMethod(clang::CXXMethodDecl *decl) {
auto find_overridden_methods =
[&decls, decl](const clang::CXXBaseSpecifier *specifier,
clang::CXXBasePath &path) {
- if (auto *base_record = llvm::dyn_cast<clang::CXXRecordDecl>(
- specifier->getType()->castAs<clang::RecordType>()->getDecl())) {
+ if (auto *base_record = specifier->getType()->getAsCXXRecordDecl()) {
clang::DeclarationName name = decl->getDeclName();
@@ -1179,7 +1179,7 @@ CompilerType TypeSystemClang::GetTypeForDecl(clang::NamedDecl *decl) {
}
CompilerType TypeSystemClang::GetTypeForDecl(TagDecl *decl) {
- return GetType(getASTContext().getTagDeclType(decl));
+ return GetType(getASTContext().getCanonicalTagType(decl));
}
CompilerType TypeSystemClang::GetTypeForDecl(ObjCInterfaceDecl *decl) {
@@ -1306,7 +1306,7 @@ CompilerType TypeSystemClang::CreateRecordType(
if (decl_ctx)
decl_ctx->addDecl(decl);
- return GetType(ast.getTagDeclType(decl));
+ return GetType(ast.getCanonicalTagType(decl));
}
namespace {
@@ -1674,7 +1674,6 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl(
class_template_specialization_decl->setInstantiationOf(class_template_decl);
class_template_specialization_decl->setTemplateArgs(
TemplateArgumentList::CreateCopy(ast, args));
- ast.getTypeDeclType(class_template_specialization_decl, nullptr);
class_template_specialization_decl->setDeclName(
class_template_decl->getDeclName());
@@ -1696,7 +1695,7 @@ CompilerType TypeSystemClang::CreateClassTemplateSpecializationType(
ClassTemplateSpecializationDecl *class_template_specialization_decl) {
if (class_template_specialization_decl) {
ASTContext &ast = getASTContext();
- return GetType(ast.getTagDeclType(class_template_specialization_decl));
+ return GetType(ast.getCanonicalTagType(class_template_specialization_decl));
}
return CompilerType();
}
@@ -1792,9 +1791,7 @@ bool TypeSystemClang::RecordHasFields(const RecordDecl *record_decl) {
for (base_class = cxx_record_decl->bases_begin(),
base_class_end = cxx_record_decl->bases_end();
base_class != base_class_end; ++base_class) {
- const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>(
- base_class->getType()->getAs<RecordType>()->getDecl());
- if (RecordHasFields(base_class_decl))
+ if (RecordHasFields(base_class->getType()->getAsCXXRecordDecl()))
return true;
}
}
@@ -2290,9 +2287,9 @@ CompilerType TypeSystemClang::CreateStructForIdentifier(
&type_fields,
bool packed) {
CompilerType type;
- if (!type_name.empty() &&
- (type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name))
- .IsValid()) {
+ if (!type_name.empty() && (type = GetTypeForIdentifier<clang::CXXRecordDecl>(
+ getASTContext(), type_name))
+ .IsValid()) {
lldbassert(0 && "Trying to create a type for an existing name");
return type;
}
@@ -2316,7 +2313,9 @@ CompilerType TypeSystemClang::GetOrCreateStructForIdentifier(
&type_fields,
bool packed) {
CompilerType type;
- if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name)).IsValid())
+ if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(getASTContext(),
+ type_name))
+ .IsValid())
return type;
return CreateStructForIdentifier(type_name, type_fields, packed);
@@ -2355,7 +2354,7 @@ CompilerType TypeSystemClang::CreateEnumerationType(
enum_decl->setAccess(AS_public); // TODO respect what's in the debug info
- return GetType(ast.getTagDeclType(enum_decl));
+ return GetType(ast.getCanonicalTagType(enum_decl));
}
CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size,
@@ -2471,7 +2470,7 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast,
ast_source->CompleteType(tag_decl);
- return !tag_decl->getTypeForDecl()->isIncompleteType();
+ return !ast->getCanonicalTagType(tag_decl)->isIncompleteType();
} else if (clang::ObjCInterfaceDecl *objc_interface_decl =
llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl)) {
if (objc_interface_decl->getDefinition())
@@ -2575,7 +2574,6 @@ RemoveWrappingTypes(QualType type, ArrayRef<clang::Type::TypeClass> mask = {}) {
break;
case clang::Type::Auto:
case clang::Type::Decltype:
- case clang::Type::Elaborated:
case clang::Type::Paren:
case clang::Type::SubstTemplateTypeParm:
case clang::Type::TemplateSpecialization:
@@ -2607,10 +2605,11 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) {
return GetDeclContextForType(
llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr())
->getPointeeType());
- case clang::Type::Record:
- return llvm::cast<clang::RecordType>(qual_type)->getDecl();
case clang::Type::Enum:
- return llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ case clang::Type::Record:
+ return llvm::cast<clang::TagType>(qual_type)
+ ->getOriginalDecl()
+ ->getDefinitionOrSelf();
default:
break;
}
@@ -2790,7 +2789,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast,
if (ast->getTargetInfo().getCXXABI().isMicrosoft()) {
auto *MPT = qual_type.getTypePtr()->castAs<clang::MemberPointerType>();
if (auto *RD = MPT->getMostRecentCXXRecordDecl())
- GetCompleteRecordType(ast, QualType(RD->getTypeForDecl(), 0),
+ GetCompleteRecordType(ast, ast->getCanonicalTagType(RD),
allow_completion);
return !qual_type.getTypePtr()->isIncompleteType();
@@ -2859,7 +2858,8 @@ bool TypeSystemClang::IsAnonymousType(lldb::opaque_compiler_type_t type) {
if (const clang::RecordType *record_type =
llvm::dyn_cast_or_null<clang::RecordType>(
qual_type.getTypePtrOrNull())) {
- if (const clang::RecordDecl *record_decl = record_type->getDecl()) {
+ if (const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()) {
return record_decl->isAnonymousStructOrUnion();
}
}
@@ -3099,8 +3099,8 @@ TypeSystemClang::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type,
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
- const clang::RecordDecl *record_decl = record_type->getDecl();
- if (record_decl) {
+ if (const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinition()) {
// We are looking for a structure that contains only floating point
// types
clang::RecordDecl::field_iterator field_pos,
@@ -3280,7 +3280,10 @@ bool TypeSystemClang::IsEnumerationType(lldb::opaque_compiler_type_t type,
GetCanonicalQualType(type)->getCanonicalTypeInternal());
if (enum_type) {
- IsIntegerType(enum_type->getDecl()->getIntegerType().getAsOpaquePtr(),
+ IsIntegerType(enum_type->getOriginalDecl()
+ ->getDefinitionOrSelf()
+ ->getIntegerType()
+ .getAsOpaquePtr(),
is_signed);
return true;
}
@@ -3505,8 +3508,7 @@ bool TypeSystemClang::IsDefined(lldb::opaque_compiler_type_t type) {
const clang::TagType *tag_type =
llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr());
if (tag_type) {
- clang::TagDecl *tag_decl = tag_type->getDecl();
- if (tag_decl)
+ if (clang::TagDecl *tag_decl = tag_type->getOriginalDecl()->getDefinition())
return tag_decl->isCompleteDefinition();
return false;
} else {
@@ -3565,21 +3567,14 @@ bool TypeSystemClang::IsPolymorphicClass(lldb::opaque_compiler_type_t type) {
switch (type_class) {
case clang::Type::Record:
if (GetCompleteType(type)) {
- const clang::RecordType *record_type =
- llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
- if (record_decl) {
- const clang::CXXRecordDecl *cxx_record_decl =
- llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
- if (cxx_record_decl) {
- // We can't just call is isPolymorphic() here because that just
- // means the current class has virtual functions, it doesn't check
- // if any inherited classes have virtual functions. The doc string
- // in SBType::IsPolymorphicClass() says it is looking for both
- // if the class has virtual methods or if any bases do, so this
- // should be more correct.
- return cxx_record_decl->isDynamicClass();
- }
+ if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl()) {
+ // We can't just call is isPolymorphic() here because that just
+ // means the current class has virtual functions, it doesn't check
+ // if any inherited classes have virtual functions. The doc string
+ // in SBType::IsPolymorphicClass() says it is looking for both
+ // if the class has virtual methods or if any bases do, so this
+ // should be more correct.
+ return cxx_record_decl->isDynamicClass();
}
}
break;
@@ -3766,7 +3761,7 @@ bool TypeSystemClang::IsBeingDefined(lldb::opaque_compiler_type_t type) {
clang::QualType qual_type(GetCanonicalQualType(type));
const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(qual_type);
if (tag_type)
- return tag_type->isBeingDefined();
+ return tag_type->getOriginalDecl()->isEntityBeingDefined();
return false;
}
@@ -3974,7 +3969,8 @@ TypeSystemClang::GetTypeInfo(lldb::opaque_compiler_type_t type,
if (pointee_or_element_clang_type)
pointee_or_element_clang_type->SetCompilerType(
weak_from_this(), llvm::cast<clang::EnumType>(qual_type)
- ->getDecl()
+ ->getOriginalDecl()
+ ->getDefinitionOrSelf()
->getIntegerType()
.getAsOpaquePtr());
return eTypeIsEnumeration | eTypeHasValue;
@@ -4154,7 +4150,6 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) {
case clang::Type::Auto:
case clang::Type::CountAttributed:
case clang::Type::Decltype:
- case clang::Type::Elaborated:
case clang::Type::Paren:
case clang::Type::TypeOf:
case clang::Type::TypeOfExpr:
@@ -4214,7 +4209,7 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) {
case clang::Type::Record: {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl = record_type->getOriginalDecl();
if (record_decl->isUnion())
return lldb::eTypeClassUnion;
else if (record_decl->isStruct())
@@ -4280,6 +4275,8 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) {
break;
case clang::Type::HLSLInlineSpirv:
break;
+ case clang::Type::SubstBuiltinTemplatePack:
+ break;
}
// We don't know hot to display this type...
return lldb::eTypeClassOther;
@@ -4412,17 +4409,10 @@ TypeSystemClang::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) {
clang::QualType qual_type = RemoveWrappingTypes(GetCanonicalQualType(type));
switch (qual_type->getTypeClass()) {
case clang::Type::Record:
- if (GetCompleteQualType(&getASTContext(), qual_type)) {
- const clang::RecordType *record_type =
- llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
- assert(record_decl);
- const clang::CXXRecordDecl *cxx_record_decl =
- llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
- if (cxx_record_decl)
+ if (GetCompleteQualType(&getASTContext(), qual_type))
+ if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl())
num_functions = std::distance(cxx_record_decl->method_begin(),
cxx_record_decl->method_end());
- }
break;
case clang::Type::ObjCObjectPointer: {
@@ -4477,13 +4467,7 @@ TypeSystemClang::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type,
switch (qual_type->getTypeClass()) {
case clang::Type::Record:
if (GetCompleteQualType(&getASTContext(), qual_type)) {
- const clang::RecordType *record_type =
- llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
- assert(record_decl);
- const clang::CXXRecordDecl *cxx_record_decl =
- llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
- if (cxx_record_decl) {
+ if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl()) {
auto method_iter = cxx_record_decl->method_begin();
auto method_end = cxx_record_decl->method_end();
if (idx <
@@ -4703,9 +4687,9 @@ CompilerType TypeSystemClang::CreateTypedef(
clang::TagDecl *tdecl = nullptr;
if (!qual_type.isNull()) {
if (const clang::RecordType *rt = qual_type->getAs<clang::RecordType>())
- tdecl = rt->getDecl();
+ tdecl = rt->getOriginalDecl();
if (const clang::EnumType *et = qual_type->getAs<clang::EnumType>())
- tdecl = et->getDecl();
+ tdecl = et->getOriginalDecl();
}
// Check whether this declaration is an anonymous struct, union, or enum,
@@ -4717,7 +4701,10 @@ CompilerType TypeSystemClang::CreateTypedef(
decl->setAccess(clang::AS_public); // TODO respect proper access specifier
// Get a uniqued clang::QualType for the typedef decl type
- return GetType(clang_ast.getTypedefType(decl));
+ NestedNameSpecifier Qualifier =
+ clang::TypeName::getFullyQualifiedDeclaredContext(clang_ast, decl);
+ return GetType(
+ clang_ast.getTypedefType(ElaboratedTypeKeyword::None, Qualifier, decl));
}
return CompilerType();
}
@@ -4869,7 +4856,6 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
case clang::Type::Auto:
case clang::Type::CountAttributed:
case clang::Type::Decltype:
- case clang::Type::Elaborated:
case clang::Type::Paren:
case clang::Type::Typedef:
case clang::Type::TypeOf:
@@ -5155,6 +5141,8 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
break;
case clang::Type::HLSLInlineSpirv:
break;
+ case clang::Type::SubstBuiltinTemplatePack:
+ break;
}
count = 0;
return lldb::eEncodingInvalid;
@@ -5171,7 +5159,6 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) {
case clang::Type::Auto:
case clang::Type::CountAttributed:
case clang::Type::Decltype:
- case clang::Type::Elaborated:
case clang::Type::Paren:
case clang::Type::Typedef:
case clang::Type::TypeOf:
@@ -5324,6 +5311,8 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) {
break;
case clang::Type::HLSLInlineSpirv:
break;
+ case clang::Type::SubstBuiltinTemplatePack:
+ break;
}
// We don't know hot to display this type...
return lldb::eFormatBytes;
@@ -5380,8 +5369,8 @@ TypeSystemClang::GetNumChildren(lldb::opaque_compiler_type_t type,
if (GetCompleteQualType(&getASTContext(), qual_type)) {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
- assert(record_decl);
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
const clang::CXXRecordDecl *cxx_record_decl =
llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
@@ -5577,7 +5566,8 @@ void TypeSystemClang::ForEachEnumerator(
const clang::EnumType *enum_type =
llvm::dyn_cast<clang::EnumType>(GetCanonicalQualType(type));
if (enum_type) {
- const clang::EnumDecl *enum_decl = enum_type->getDecl();
+ const clang::EnumDecl *enum_decl =
+ enum_type->getOriginalDecl()->getDefinitionOrSelf();
if (enum_decl) {
CompilerType integer_type = GetType(enum_decl->getIntegerType());
@@ -5608,7 +5598,8 @@ uint32_t TypeSystemClang::GetNumFields(lldb::opaque_compiler_type_t type) {
const clang::RecordType *record_type =
llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
- clang::RecordDecl *record_decl = record_type->getDecl();
+ clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinition();
if (record_decl) {
count = std::distance(record_decl->field_begin(),
record_decl->field_end());
@@ -5722,7 +5713,8 @@ CompilerType TypeSystemClang::GetFieldAtIndex(lldb::opaque_compiler_type_t type,
if (GetCompleteType(type)) {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
uint32_t field_idx = 0;
clang::RecordDecl::field_iterator field, field_end;
for (field = record_decl->field_begin(),
@@ -5908,7 +5900,7 @@ CompilerType TypeSystemClang::GetDirectBaseClassAtIndex(
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getDecl());
+ ->getOriginalDecl());
if (base_class->isVirtual())
*bit_offset_ptr =
record_layout.getVBaseClassOffset(base_class_decl)
@@ -6003,7 +5995,7 @@ CompilerType TypeSystemClang::GetVirtualBaseClassAtIndex(
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getDecl());
+ ->getOriginalDecl());
*bit_offset_ptr =
record_layout.getVBaseClassOffset(base_class_decl)
.getQuantity() *
@@ -6033,7 +6025,8 @@ TypeSystemClang::GetStaticFieldWithName(lldb::opaque_compiler_type_t type,
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
clang::DeclarationName decl_name(&getASTContext().Idents.get(name));
for (NamedDecl *decl : record_decl->lookup(decl_name)) {
@@ -6263,8 +6256,8 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
if (idx_is_valid && GetCompleteType(type)) {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
- assert(record_decl);
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
const clang::ASTRecordLayout &record_layout =
getASTContext().getASTRecordLayout(record_decl);
uint32_t child_idx = 0;
@@ -6283,7 +6276,10 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
// Skip empty base classes
if (omit_empty_base_classes) {
base_class_decl = llvm::cast<clang::CXXRecordDecl>(
- base_class->getType()->getAs<clang::RecordType>()->getDecl());
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getOriginalDecl())
+ ->getDefinitionOrSelf();
if (!TypeSystemClang::RecordHasFields(base_class_decl))
continue;
}
@@ -6291,7 +6287,10 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
if (idx == child_idx) {
if (base_class_decl == nullptr)
base_class_decl = llvm::cast<clang::CXXRecordDecl>(
- base_class->getType()->getAs<clang::RecordType>()->getDecl());
+ base_class->getType()
+ ->getAs<clang::RecordType>()
+ ->getOriginalDecl())
+ ->getDefinitionOrSelf();
if (base_class->isVirtual()) {
bool handled = false;
@@ -6752,7 +6751,8 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
if (GetCompleteType(type)) {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
assert(record_decl);
uint32_t child_idx = 0;
@@ -6817,10 +6817,10 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
return 0;
} else {
child_indexes.push_back(child_idx);
- parent_record_decl = llvm::cast<clang::RecordDecl>(
- elem.Base->getType()
- ->castAs<clang::RecordType>()
- ->getDecl());
+ parent_record_decl = elem.Base->getType()
+ ->castAs<clang::RecordType>()
+ ->getOriginalDecl()
+ ->getDefinitionOrSelf();
}
}
for (clang::DeclContext::lookup_iterator I = path->Decls, E;
@@ -6954,7 +6954,8 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
if (GetCompleteType(type)) {
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
assert(record_decl);
uint32_t child_idx = 0;
@@ -6973,7 +6974,8 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getDecl());
+ ->getOriginalDecl())
+ ->getDefinitionOrSelf();
if (omit_empty_base_classes &&
!TypeSystemClang::RecordHasFields(base_class_decl))
continue;
@@ -7092,14 +7094,17 @@ TypeSystemClang::GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type,
return CompilerType();
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
clang::DeclarationName decl_name(&getASTContext().Idents.get(name));
for (NamedDecl *decl : record_decl->lookup(decl_name)) {
if (auto *tag_decl = dyn_cast<clang::TagDecl>(decl))
- return GetType(getASTContext().getTagDeclType(tag_decl));
+ return GetType(getASTContext().getCanonicalTagType(tag_decl));
if (auto *typedef_decl = dyn_cast<clang::TypedefNameDecl>(decl))
- return GetType(getASTContext().getTypedefType(typedef_decl));
+ return GetType(getASTContext().getTypedefType(
+ ElaboratedTypeKeyword::None, /*Qualifier=*/std::nullopt,
+ typedef_decl));
}
break;
}
@@ -7116,7 +7121,7 @@ bool TypeSystemClang::IsTemplateType(lldb::opaque_compiler_type_t type) {
const clang::Type *clang_type = ClangUtil::GetQualType(ct).getTypePtr();
if (auto *cxx_record_decl = dyn_cast<clang::TagType>(clang_type))
return isa<clang::ClassTemplateSpecializationDecl>(
- cxx_record_decl->getDecl());
+ cxx_record_decl->getOriginalDecl());
return false;
}
@@ -7319,7 +7324,7 @@ clang::EnumDecl *TypeSystemClang::GetAsEnumDecl(const CompilerType &type) {
const clang::EnumType *enutype =
llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type));
if (enutype)
- return enutype->getDecl();
+ return enutype->getOriginalDecl()->getDefinitionOrSelf();
return nullptr;
}
@@ -7327,7 +7332,7 @@ clang::RecordDecl *TypeSystemClang::GetAsRecordDecl(const CompilerType &type) {
const clang::RecordType *record_type =
llvm::dyn_cast<clang::RecordType>(ClangUtil::GetCanonicalQualType(type));
if (record_type)
- return record_type->getDecl();
+ return record_type->getOriginalDecl()->getDefinitionOrSelf();
return nullptr;
}
@@ -7409,7 +7414,7 @@ clang::FieldDecl *TypeSystemClang::AddFieldToRecordType(
if (const clang::TagType *TagT =
field->getType()->getAs<clang::TagType>()) {
if (clang::RecordDecl *Rec =
- llvm::dyn_cast<clang::RecordDecl>(TagT->getDecl()))
+ llvm::dyn_cast<clang::RecordDecl>(TagT->getOriginalDecl()))
if (!Rec->getDeclName()) {
Rec->setAnonymousStructOrUnion(true);
field->setImplicit();
@@ -7494,7 +7499,8 @@ void TypeSystemClang::BuildIndirectFields(const CompilerType &type) {
if (!field_record_type)
continue;
- clang::RecordDecl *field_record_decl = field_record_type->getDecl();
+ clang::RecordDecl *field_record_decl =
+ field_record_type->getOriginalDecl()->getDefinition();
if (!field_record_decl)
continue;
@@ -7636,7 +7642,8 @@ void TypeSystemClang::SetIntegerInitializerForVariable(
// If the variable is an enum type, take the underlying integer type as
// the type of the integer literal.
if (const EnumType *enum_type = qt->getAs<EnumType>()) {
- const EnumDecl *enum_decl = enum_type->getDecl();
+ const EnumDecl *enum_decl =
+ enum_type->getOriginalDecl()->getDefinitionOrSelf();
qt = enum_decl->getIntegerType();
}
// Bools are handled separately because the clang AST printer handles bools
@@ -8296,7 +8303,7 @@ bool TypeSystemClang::SetHasExternalStorage(lldb::opaque_compiler_type_t type,
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
if (enum_decl) {
enum_decl->setHasExternalLexicalStorage(has_extern);
enum_decl->setHasExternalVisibleStorage(has_extern);
@@ -8334,7 +8341,7 @@ bool TypeSystemClang::StartTagDeclarationDefinition(const CompilerType &type) {
if (!qual_type.isNull()) {
const clang::TagType *tag_type = qual_type->getAs<clang::TagType>();
if (tag_type) {
- clang::TagDecl *tag_decl = tag_type->getDecl();
+ clang::TagDecl *tag_decl = tag_type->getOriginalDecl();
if (tag_decl) {
tag_decl->startDefinition();
return true;
@@ -8369,7 +8376,8 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition(
// the definition.
const clang::TagType *tag_type = qual_type->getAs<clang::TagType>();
if (tag_type) {
- clang::TagDecl *tag_decl = tag_type->getDecl();
+ clang::TagDecl *tag_decl =
+ tag_type->getOriginalDecl()->getDefinitionOrSelf();
if (auto *cxx_record_decl = llvm::dyn_cast<CXXRecordDecl>(tag_decl)) {
// If we have a move constructor declared but no copy constructor we
@@ -8404,7 +8412,8 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition(
if (!enutype)
return false;
- clang::EnumDecl *enum_decl = enutype->getDecl();
+ clang::EnumDecl *enum_decl =
+ enutype->getOriginalDecl()->getDefinitionOrSelf();
if (enum_decl->isCompleteDefinition())
return true;
@@ -8462,17 +8471,19 @@ clang::EnumConstantDecl *TypeSystemClang::AddEnumerationValueToEnumerationType(
clang::EnumConstantDecl *enumerator_decl =
clang::EnumConstantDecl::CreateDeserialized(getASTContext(),
GlobalDeclID());
- enumerator_decl->setDeclContext(enutype->getDecl());
+ clang::EnumDecl *enum_decl =
+ enutype->getOriginalDecl()->getDefinitionOrSelf();
+ enumerator_decl->setDeclContext(enum_decl);
if (name && name[0])
enumerator_decl->setDeclName(&getASTContext().Idents.get(name));
enumerator_decl->setType(clang::QualType(enutype, 0));
enumerator_decl->setInitVal(getASTContext(), value);
- SetMemberOwningModule(enumerator_decl, enutype->getDecl());
+ SetMemberOwningModule(enumerator_decl, enum_decl);
if (!enumerator_decl)
return nullptr;
- enutype->getDecl()->addDecl(enumerator_decl);
+ enum_decl->addDecl(enumerator_decl);
VerifyDecl(enumerator_decl);
return enumerator_decl;
@@ -8496,7 +8507,8 @@ CompilerType TypeSystemClang::GetEnumerationIntegerType(CompilerType type) {
if (!enum_type)
return CompilerType();
- return GetType(enum_type->getDecl()->getIntegerType());
+ return GetType(
+ enum_type->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType());
}
CompilerType
@@ -8509,7 +8521,7 @@ TypeSystemClang::CreateMemberPointerType(const CompilerType &type,
return CompilerType();
return ast->GetType(ast->getASTContext().getMemberPointerType(
ClangUtil::GetQualType(pointee_type),
- /*Qualifier=*/nullptr,
+ /*Qualifier=*/std::nullopt,
ClangUtil::GetQualType(type)->getAsCXXRecordDecl()));
}
return CompilerType();
@@ -8587,8 +8599,8 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
uint32_t bitfield_bit_size) {
const clang::EnumType *enutype =
llvm::cast<clang::EnumType>(qual_type.getTypePtr());
- const clang::EnumDecl *enum_decl = enutype->getDecl();
- assert(enum_decl);
+ const clang::EnumDecl *enum_decl =
+ enutype->getOriginalDecl()->getDefinitionOrSelf();
lldb::offset_t offset = byte_offset;
bool qual_type_is_signed = qual_type->isSignedIntegerOrEnumerationType();
const uint64_t enum_svalue =
@@ -8694,15 +8706,7 @@ bool TypeSystemClang::DumpTypeValue(
} else {
clang::QualType qual_type(GetQualType(type));
- const clang::Type::TypeClass type_class = qual_type->getTypeClass();
-
- if (type_class == clang::Type::Elaborated) {
- qual_type = llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType();
- return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data, byte_offset, byte_size,
- bitfield_bit_size, bitfield_bit_offset, exe_scope);
- }
-
- switch (type_class) {
+ switch (qual_type->getTypeClass()) {
case clang::Type::Typedef: {
clang::QualType typedef_qual_type =
llvm::cast<clang::TypedefType>(qual_type)
@@ -8872,7 +8876,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
GetCompleteType(type);
auto *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr());
- const clang::RecordDecl *record_decl = record_type->getDecl();
+ const clang::RecordDecl *record_decl = record_type->getOriginalDecl();
if (level == eDescriptionLevelVerbose)
record_decl->dump(llvm_ostrm);
else {
@@ -8884,7 +8888,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type,
default: {
if (auto *tag_type =
llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr())) {
- if (clang::TagDecl *tag_decl = tag_type->getDecl()) {
+ if (clang::TagDecl *tag_decl = tag_type->getOriginalDecl()) {
if (level == eDescriptionLevelVerbose)
tag_decl->dump(llvm_ostrm);
else
@@ -8924,7 +8928,7 @@ void TypeSystemClang::DumpTypeName(const CompilerType &type) {
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
if (enum_decl) {
printf("enum %s", enum_decl->getName().str().c_str());
}
@@ -8960,13 +8964,6 @@ void TypeSystemClang::DumpTypeName(const CompilerType &type) {
->getDeducedType()
.getAsOpaquePtr()));
- case clang::Type::Elaborated:
- printf("elaborated ");
- return DumpTypeName(CompilerType(
- type.GetTypeSystem(), llvm::cast<clang::ElaboratedType>(qual_type)
- ->getNamedType()
- .getAsOpaquePtr()));
-
case clang::Type::Paren:
printf("paren ");
return DumpTypeName(CompilerType(
@@ -9796,8 +9793,8 @@ bool TypeSystemClang::IsForcefullyCompleted(lldb::opaque_compiler_type_t type) {
const clang::RecordType *record_type =
llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
- const clang::RecordDecl *record_decl = record_type->getDecl();
- assert(record_decl);
+ const clang::RecordDecl *record_decl =
+ record_type->getOriginalDecl()->getDefinitionOrSelf();
if (std::optional<ClangASTMetadata> metadata = GetMetadata(record_decl))
return metadata->IsForcefullyCompleted();
}
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 70d613d..709f8959 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -260,7 +260,7 @@ public:
template <typename RecordDeclType>
CompilerType
- GetTypeForIdentifier(llvm::StringRef type_name,
+ GetTypeForIdentifier(const clang::ASTContext &Ctx, llvm::StringRef type_name,
clang::DeclContext *decl_context = nullptr) {
CompilerType compiler_type;
if (type_name.empty())
@@ -278,11 +278,10 @@ public:
return compiler_type;
clang::NamedDecl *named_decl = *result.begin();
- if (const RecordDeclType *record_decl =
- llvm::dyn_cast<RecordDeclType>(named_decl))
+ if (const auto *type_decl = llvm::dyn_cast<clang::TypeDecl>(named_decl);
+ llvm::isa_and_nonnull<RecordDeclType>(type_decl))
compiler_type = CompilerType(
- weak_from_this(),
- clang::QualType(record_decl->getTypeForDecl(), 0).getAsOpaquePtr());
+ weak_from_this(), Ctx.getTypeDeclType(type_decl).getAsOpaquePtr());
return compiler_type;
}
diff --git a/lldb/source/Protocol/MCP/CMakeLists.txt b/lldb/source/Protocol/MCP/CMakeLists.txt
index f1b1098..a4f270a 100644
--- a/lldb/source/Protocol/MCP/CMakeLists.txt
+++ b/lldb/source/Protocol/MCP/CMakeLists.txt
@@ -1,11 +1,13 @@
add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES
MCPError.cpp
Protocol.cpp
+ Server.cpp
Tool.cpp
LINK_COMPONENTS
Support
LINK_LIBS
+ lldbHost
lldbUtility
)
diff --git a/lldb/source/Protocol/MCP/MCPError.cpp b/lldb/source/Protocol/MCP/MCPError.cpp
index c610e88..e140d11 100644
--- a/lldb/source/Protocol/MCP/MCPError.cpp
+++ b/lldb/source/Protocol/MCP/MCPError.cpp
@@ -25,10 +25,10 @@ std::error_code MCPError::convertToErrorCode() const {
return llvm::inconvertibleErrorCode();
}
-lldb_protocol::mcp::Error MCPError::toProtcolError() const {
+lldb_protocol::mcp::Error MCPError::toProtocolError() const {
lldb_protocol::mcp::Error error;
- error.error.code = m_error_code;
- error.error.message = m_message;
+ error.code = m_error_code;
+ error.message = m_message;
return error;
}
diff --git a/lldb/source/Protocol/MCP/Protocol.cpp b/lldb/source/Protocol/MCP/Protocol.cpp
index d579b88..0988f45 100644
--- a/lldb/source/Protocol/MCP/Protocol.cpp
+++ b/lldb/source/Protocol/MCP/Protocol.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/JSON.h"
using namespace llvm;
@@ -26,8 +27,45 @@ static bool mapRaw(const json::Value &Params, StringLiteral Prop,
return true;
}
+static llvm::json::Value toJSON(const Id &Id) {
+ if (const int64_t *I = std::get_if<int64_t>(&Id))
+ return json::Value(*I);
+ if (const std::string *S = std::get_if<std::string>(&Id))
+ return json::Value(*S);
+ llvm_unreachable("unexpected type in protocol::Id");
+}
+
+static bool mapId(const llvm::json::Value &V, StringLiteral Prop, Id &Id,
+ llvm::json::Path P) {
+ const auto *O = V.getAsObject();
+ if (!O) {
+ P.report("expected object");
+ return false;
+ }
+
+ const auto *E = O->get(Prop);
+ if (!E) {
+ P.field(Prop).report("not found");
+ return false;
+ }
+
+ if (auto S = E->getAsString()) {
+ Id = S->str();
+ return true;
+ }
+
+ if (auto I = E->getAsInteger()) {
+ Id = *I;
+ return true;
+ }
+
+ P.report("expected string or number");
+ return false;
+}
+
llvm::json::Value toJSON(const Request &R) {
- json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}, {"method", R.method}};
+ json::Object Result{
+ {"jsonrpc", "2.0"}, {"id", toJSON(R.id)}, {"method", R.method}};
if (R.params)
Result.insert({"params", R.params});
return Result;
@@ -35,47 +73,75 @@ llvm::json::Value toJSON(const Request &R) {
bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) {
llvm::json::ObjectMapper O(V, P);
- if (!O || !O.map("id", R.id) || !O.map("method", R.method))
- return false;
- return mapRaw(V, "params", R.params, P);
+ return O && mapId(V, "id", R.id, P) && O.map("method", R.method) &&
+ mapRaw(V, "params", R.params, P);
}
-llvm::json::Value toJSON(const ErrorInfo &EI) {
- llvm::json::Object Result{{"code", EI.code}, {"message", EI.message}};
- if (!EI.data.empty())
- Result.insert({"data", EI.data});
- return Result;
-}
-
-bool fromJSON(const llvm::json::Value &V, ErrorInfo &EI, llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("code", EI.code) && O.map("message", EI.message) &&
- O.mapOptional("data", EI.data);
+bool operator==(const Request &a, const Request &b) {
+ return a.id == b.id && a.method == b.method && a.params == b.params;
}
llvm::json::Value toJSON(const Error &E) {
- return json::Object{{"jsonrpc", "2.0"}, {"id", E.id}, {"error", E.error}};
+ llvm::json::Object Result{{"code", E.code}, {"message", E.message}};
+ if (E.data)
+ Result.insert({"data", *E.data});
+ return Result;
}
bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) {
llvm::json::ObjectMapper O(V, P);
- return O && O.map("id", E.id) && O.map("error", E.error);
+ return O && O.map("code", E.code) && O.map("message", E.message) &&
+ mapRaw(V, "data", E.data, P);
+}
+
+bool operator==(const Error &a, const Error &b) {
+ return a.code == b.code && a.message == b.message && a.data == b.data;
}
llvm::json::Value toJSON(const Response &R) {
- llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}};
- if (R.result)
- Result.insert({"result", R.result});
- if (R.error)
- Result.insert({"error", R.error});
+ llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", toJSON(R.id)}};
+
+ if (const Error *error = std::get_if<Error>(&R.result))
+ Result.insert({"error", *error});
+ if (const json::Value *result = std::get_if<json::Value>(&R.result))
+ Result.insert({"result", *result});
return Result;
}
bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- if (!O || !O.map("id", R.id) || !O.map("error", R.error))
+ const json::Object *E = V.getAsObject();
+ if (!E) {
+ P.report("expected object");
+ return false;
+ }
+
+ const json::Value *result = E->get("result");
+ const json::Value *raw_error = E->get("error");
+
+ if (result && raw_error) {
+ P.report("'result' and 'error' fields are mutually exclusive");
return false;
- return mapRaw(V, "result", R.result, P);
+ }
+
+ if (!result && !raw_error) {
+ P.report("'result' or 'error' fields are required'");
+ return false;
+ }
+
+ if (result) {
+ R.result = std::move(*result);
+ } else {
+ Error error;
+ if (!fromJSON(*raw_error, error, P))
+ return false;
+ R.result = std::move(error);
+ }
+
+ return mapId(V, "id", R.id, P);
+}
+
+bool operator==(const Response &a, const Response &b) {
+ return a.id == b.id && a.result == b.result;
}
llvm::json::Value toJSON(const Notification &N) {
@@ -97,30 +163,8 @@ bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) {
return true;
}
-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 operator==(const Notification &a, const Notification &b) {
+ return a.method == b.method && a.params == b.params;
}
bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) {
@@ -139,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);
@@ -177,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())
@@ -235,24 +265,16 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) {
return true;
}
- if (O->get("error")) {
- Error E;
- if (!fromJSON(V, E, P))
- return false;
- M = std::move(E);
- return true;
- }
-
- if (O->get("result")) {
- Response R;
+ if (O->get("method")) {
+ Request R;
if (!fromJSON(V, R, P))
return false;
M = std::move(R);
return true;
}
- if (O->get("method")) {
- Request R;
+ if (O->get("result") || O->get("error")) {
+ Response R;
if (!fromJSON(V, R, P))
return false;
M = std::move(R);
@@ -263,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
new file mode 100644
index 0000000..0381b7f
--- /dev/null
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -0,0 +1,264 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Server.h"
+#include "lldb/Protocol/MCP/MCPError.h"
+#include "lldb/Protocol/MCP/Protocol.h"
+#include "llvm/Support/JSON.h"
+
+using namespace lldb_protocol::mcp;
+using namespace llvm;
+
+llvm::json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) {
+ return llvm::json::Object{{"connection_uri", SM.connection_uri},
+ {"pid", SM.pid}};
+}
+
+bool lldb_protocol::mcp::fromJSON(const llvm::json::Value &V, ServerInfo &SM,
+ llvm::json::Path P) {
+ llvm::json::ObjectMapper O(V, P);
+ return O && O.map("connection_uri", SM.connection_uri) &&
+ O.map("pid", SM.pid);
+}
+
+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();
+}
+
+void Server::AddRequestHandlers() {
+ AddRequestHandler("initialize", std::bind(&Server::InitializeHandler, this,
+ std::placeholders::_1));
+ AddRequestHandler("tools/list", std::bind(&Server::ToolsListHandler, this,
+ std::placeholders::_1));
+ AddRequestHandler("tools/call", std::bind(&Server::ToolsCallHandler, this,
+ std::placeholders::_1));
+ AddRequestHandler("resources/list", std::bind(&Server::ResourcesListHandler,
+ this, std::placeholders::_1));
+ AddRequestHandler("resources/read", std::bind(&Server::ResourcesReadHandler,
+ this, std::placeholders::_1));
+}
+
+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);
+ if (!response)
+ return response;
+ response->id = request.id;
+ return *response;
+ }
+
+ return llvm::make_error<MCPError>(
+ llvm::formatv("no handler for request: {0}", request.method).str());
+}
+
+void Server::Handle(const Notification &notification) {
+ auto it = m_notification_handlers.find(notification.method);
+ if (it != m_notification_handlers.end()) {
+ it->second(notification);
+ return;
+ }
+}
+
+void Server::AddTool(std::unique_ptr<Tool> tool) {
+ if (!tool)
+ return;
+ m_tools[tool->GetName()] = std::move(tool);
+}
+
+void Server::AddResourceProvider(
+ std::unique_ptr<ResourceProvider> resource_provider) {
+ if (!resource_provider)
+ return;
+ m_resource_providers.push_back(std::move(resource_provider));
+}
+
+void Server::AddRequestHandler(llvm::StringRef method, RequestHandler handler) {
+ m_request_handlers[method] = std::move(handler);
+}
+
+void Server::AddNotificationHandler(llvm::StringRef method,
+ NotificationHandler handler) {
+ m_notification_handlers[method] = std::move(handler);
+}
+
+llvm::Expected<Response> Server::InitializeHandler(const Request &request) {
+ Response response;
+ 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;
+
+ ListToolsResult result;
+ for (const auto &tool : m_tools)
+ result.tools.emplace_back(tool.second->GetDefinition());
+
+ response.result = std::move(result);
+
+ return response;
+}
+
+llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) {
+ Response response;
+
+ 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();
+
+ llvm::StringRef tool_name = params.name;
+ if (tool_name.empty())
+ return llvm::createStringError("no tool name");
+
+ auto it = m_tools.find(tool_name);
+ if (it == m_tools.end())
+ return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name));
+
+ ToolArguments tool_args;
+ if (params.arguments)
+ tool_args = *params.arguments;
+
+ llvm::Expected<CallToolResult> text_result = it->second->Call(tool_args);
+ if (!text_result)
+ return text_result.takeError();
+
+ response.result = toJSON(*text_result);
+
+ return response;
+}
+
+llvm::Expected<Response> Server::ResourcesListHandler(const Request &request) {
+ Response response;
+
+ ListResourcesResult result;
+ for (std::unique_ptr<ResourceProvider> &resource_provider_up :
+ m_resource_providers)
+ for (const Resource &resource : resource_provider_up->GetResources())
+ result.resources.push_back(resource);
+
+ response.result = std::move(result);
+
+ return response;
+}
+
+llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) {
+ Response response;
+
+ if (!request.params)
+ return llvm::createStringError("no resource parameters");
+
+ ReadResourceParams params;
+ json::Path::Root root("params");
+ if (!fromJSON(request.params, params, root))
+ return root.getError();
+
+ llvm::StringRef uri_str = params.uri;
+ if (uri_str.empty())
+ return llvm::createStringError("no resource uri");
+
+ for (std::unique_ptr<ResourceProvider> &resource_provider_up :
+ m_resource_providers) {
+ llvm::Expected<ReadResourceResult> result =
+ resource_provider_up->ReadResource(uri_str);
+ if (result.errorIsA<UnsupportedURI>()) {
+ llvm::consumeError(result.takeError());
+ continue;
+ }
+ if (!result)
+ return result.takeError();
+
+ Response response;
+ response.result = std::move(*result);
+ return response;
+ }
+
+ return make_error<MCPError>(
+ 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/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp
index 21daf74..7efce2a 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -379,6 +379,7 @@ AddressClass ObjectFile::GetAddressClass(addr_t file_addr) {
case eSectionTypeELFDynamicSymbols:
case eSectionTypeELFRelocationEntries:
case eSectionTypeELFDynamicLinkInfo:
+ case eSectionTypeWasmName:
case eSectionTypeOther:
return AddressClass::eUnknown;
case eSectionTypeAbsoluteAddress:
diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp
index d6689a6..40497db 100644
--- a/lldb/source/Symbol/Symbol.cpp
+++ b/lldb/source/Symbol/Symbol.cpp
@@ -392,45 +392,8 @@ bool Symbol::Compare(ConstString name, SymbolType type) const {
return false;
}
-#define ENUM_TO_CSTRING(x) \
- case eSymbolType##x: \
- return #x;
-
const char *Symbol::GetTypeAsString() const {
- switch (m_type) {
- ENUM_TO_CSTRING(Invalid);
- ENUM_TO_CSTRING(Absolute);
- ENUM_TO_CSTRING(Code);
- ENUM_TO_CSTRING(Resolver);
- ENUM_TO_CSTRING(Data);
- ENUM_TO_CSTRING(Trampoline);
- ENUM_TO_CSTRING(Runtime);
- ENUM_TO_CSTRING(Exception);
- ENUM_TO_CSTRING(SourceFile);
- ENUM_TO_CSTRING(HeaderFile);
- ENUM_TO_CSTRING(ObjectFile);
- ENUM_TO_CSTRING(CommonBlock);
- ENUM_TO_CSTRING(Block);
- ENUM_TO_CSTRING(Local);
- ENUM_TO_CSTRING(Param);
- ENUM_TO_CSTRING(Variable);
- ENUM_TO_CSTRING(VariableType);
- ENUM_TO_CSTRING(LineEntry);
- ENUM_TO_CSTRING(LineHeader);
- ENUM_TO_CSTRING(ScopeBegin);
- ENUM_TO_CSTRING(ScopeEnd);
- ENUM_TO_CSTRING(Additional);
- ENUM_TO_CSTRING(Compiler);
- ENUM_TO_CSTRING(Instrumentation);
- ENUM_TO_CSTRING(Undefined);
- ENUM_TO_CSTRING(ObjCClass);
- ENUM_TO_CSTRING(ObjCMetaClass);
- ENUM_TO_CSTRING(ObjCIVar);
- ENUM_TO_CSTRING(ReExported);
- default:
- break;
- }
- return "<unknown SymbolType>";
+ return GetTypeAsString(static_cast<lldb::SymbolType>(m_type));
}
void Symbol::CalculateSymbolContext(SymbolContext *sc) {
@@ -774,6 +737,79 @@ bool Symbol::operator==(const Symbol &rhs) const {
return true;
}
+#define ENUM_TO_CSTRING(x) \
+ case eSymbolType##x: \
+ return #x;
+
+const char *Symbol::GetTypeAsString(lldb::SymbolType symbol_type) {
+ switch (symbol_type) {
+ ENUM_TO_CSTRING(Invalid);
+ ENUM_TO_CSTRING(Absolute);
+ ENUM_TO_CSTRING(Code);
+ ENUM_TO_CSTRING(Resolver);
+ ENUM_TO_CSTRING(Data);
+ ENUM_TO_CSTRING(Trampoline);
+ ENUM_TO_CSTRING(Runtime);
+ ENUM_TO_CSTRING(Exception);
+ ENUM_TO_CSTRING(SourceFile);
+ ENUM_TO_CSTRING(HeaderFile);
+ ENUM_TO_CSTRING(ObjectFile);
+ ENUM_TO_CSTRING(CommonBlock);
+ ENUM_TO_CSTRING(Block);
+ ENUM_TO_CSTRING(Local);
+ ENUM_TO_CSTRING(Param);
+ ENUM_TO_CSTRING(Variable);
+ ENUM_TO_CSTRING(VariableType);
+ ENUM_TO_CSTRING(LineEntry);
+ ENUM_TO_CSTRING(LineHeader);
+ ENUM_TO_CSTRING(ScopeBegin);
+ ENUM_TO_CSTRING(ScopeEnd);
+ ENUM_TO_CSTRING(Additional);
+ ENUM_TO_CSTRING(Compiler);
+ ENUM_TO_CSTRING(Instrumentation);
+ ENUM_TO_CSTRING(Undefined);
+ ENUM_TO_CSTRING(ObjCClass);
+ ENUM_TO_CSTRING(ObjCMetaClass);
+ ENUM_TO_CSTRING(ObjCIVar);
+ ENUM_TO_CSTRING(ReExported);
+ }
+ return "<unknown SymbolType>";
+}
+
+lldb::SymbolType Symbol::GetTypeFromString(const char *str) {
+ std::string str_lower = llvm::StringRef(str).lower();
+ return llvm::StringSwitch<lldb::SymbolType>(str_lower)
+ .Case("absolute", eSymbolTypeAbsolute)
+ .Case("code", eSymbolTypeCode)
+ .Case("resolver", eSymbolTypeResolver)
+ .Case("data", eSymbolTypeData)
+ .Case("trampoline", eSymbolTypeTrampoline)
+ .Case("runtime", eSymbolTypeRuntime)
+ .Case("exception", eSymbolTypeException)
+ .Case("sourcefile", eSymbolTypeSourceFile)
+ .Case("headerfile", eSymbolTypeHeaderFile)
+ .Case("objectfile", eSymbolTypeObjectFile)
+ .Case("commonblock", eSymbolTypeCommonBlock)
+ .Case("block", eSymbolTypeBlock)
+ .Case("local", eSymbolTypeLocal)
+ .Case("param", eSymbolTypeParam)
+ .Case("variable", eSymbolTypeVariable)
+ .Case("variableType", eSymbolTypeVariableType)
+ .Case("lineentry", eSymbolTypeLineEntry)
+ .Case("lineheader", eSymbolTypeLineHeader)
+ .Case("scopebegin", eSymbolTypeScopeBegin)
+ .Case("scopeend", eSymbolTypeScopeEnd)
+ .Case("additional,", eSymbolTypeAdditional)
+ .Case("compiler", eSymbolTypeCompiler)
+ .Case("instrumentation", eSymbolTypeInstrumentation)
+ .Case("undefined", eSymbolTypeUndefined)
+ .Case("objcclass", eSymbolTypeObjCClass)
+ .Case("objcmetaclass", eSymbolTypeObjCMetaClass)
+ .Case("objcivar", eSymbolTypeObjCIVar)
+ .Case("reexported", eSymbolTypeReExported)
+ .Default(eSymbolTypeInvalid);
+}
+
namespace llvm {
namespace json {
@@ -804,36 +840,8 @@ bool fromJSON(const llvm::json::Value &value, lldb_private::JSONSymbol &symbol,
bool fromJSON(const llvm::json::Value &value, lldb::SymbolType &type,
llvm::json::Path path) {
if (auto str = value.getAsString()) {
- type = llvm::StringSwitch<lldb::SymbolType>(*str)
- .Case("absolute", eSymbolTypeAbsolute)
- .Case("code", eSymbolTypeCode)
- .Case("resolver", eSymbolTypeResolver)
- .Case("data", eSymbolTypeData)
- .Case("trampoline", eSymbolTypeTrampoline)
- .Case("runtime", eSymbolTypeRuntime)
- .Case("exception", eSymbolTypeException)
- .Case("sourcefile", eSymbolTypeSourceFile)
- .Case("headerfile", eSymbolTypeHeaderFile)
- .Case("objectfile", eSymbolTypeObjectFile)
- .Case("commonblock", eSymbolTypeCommonBlock)
- .Case("block", eSymbolTypeBlock)
- .Case("local", eSymbolTypeLocal)
- .Case("param", eSymbolTypeParam)
- .Case("variable", eSymbolTypeVariable)
- .Case("variableType", eSymbolTypeVariableType)
- .Case("lineentry", eSymbolTypeLineEntry)
- .Case("lineheader", eSymbolTypeLineHeader)
- .Case("scopebegin", eSymbolTypeScopeBegin)
- .Case("scopeend", eSymbolTypeScopeEnd)
- .Case("additional,", eSymbolTypeAdditional)
- .Case("compiler", eSymbolTypeCompiler)
- .Case("instrumentation", eSymbolTypeInstrumentation)
- .Case("undefined", eSymbolTypeUndefined)
- .Case("objcclass", eSymbolTypeObjCClass)
- .Case("objcmetaClass", eSymbolTypeObjCMetaClass)
- .Case("objcivar", eSymbolTypeObjCIVar)
- .Case("reexporte", eSymbolTypeReExported)
- .Default(eSymbolTypeInvalid);
+ llvm::StringRef str_ref = str.value_or("");
+ type = Symbol::GetTypeFromString(str_ref.data());
if (type == eSymbolTypeInvalid) {
path.report("invalid symbol type");
diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp
index 3f54ea5..9d232e4 100644
--- a/lldb/source/Target/ExecutionContext.cpp
+++ b/lldb/source/Target/ExecutionContext.cpp
@@ -12,7 +12,9 @@
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
+#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/State.h"
+#include <mutex>
using namespace lldb_private;
@@ -125,19 +127,47 @@ ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr,
}
}
-ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr,
- std::unique_lock<std::recursive_mutex> &lock)
- : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() {
- if (exe_ctx_ref_ptr) {
- m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
- if (m_target_sp) {
- lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex());
+llvm::Expected<StoppedExecutionContext>
+lldb_private::GetStoppedExecutionContext(
+ const lldb::ExecutionContextRefSP &exe_ctx_ref_ptr) {
+ return GetStoppedExecutionContext(exe_ctx_ref_ptr.get());
+}
- m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
- m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
- m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
- }
- }
+llvm::Expected<StoppedExecutionContext>
+lldb_private::GetStoppedExecutionContext(
+ const ExecutionContextRef *exe_ctx_ref_ptr) {
+ if (!exe_ctx_ref_ptr)
+ return llvm::createStringError(
+ "StoppedExecutionContext created with an empty ExecutionContextRef");
+
+ lldb::TargetSP target_sp = exe_ctx_ref_ptr->GetTargetSP();
+ if (!target_sp)
+ return llvm::createStringError(
+ "StoppedExecutionContext created with a null target");
+
+ auto api_lock =
+ std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex());
+
+ auto process_sp = exe_ctx_ref_ptr->GetProcessSP();
+ if (!process_sp)
+ return llvm::createStringError(
+ "StoppedExecutionContext created with a null process");
+
+ ProcessRunLock::ProcessRunLocker stop_locker;
+ if (!stop_locker.TryLock(&process_sp->GetRunLock()))
+ return llvm::createStringError(
+ "attempted to create a StoppedExecutionContext with a running process");
+
+ auto thread_sp = exe_ctx_ref_ptr->GetThreadSP();
+ auto frame_sp = exe_ctx_ref_ptr->GetFrameSP();
+ return StoppedExecutionContext(target_sp, process_sp, thread_sp, frame_sp,
+ std::move(api_lock), std::move(stop_locker));
+}
+
+std::unique_lock<std::recursive_mutex> StoppedExecutionContext::AllowResume() {
+ Clear();
+ m_stop_locker = ProcessRunLock::ProcessRunLocker();
+ return std::move(m_api_lock);
}
ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr)
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index 9e9e2d8..bcf1297 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -2026,7 +2026,6 @@ bool RegisterContextUnwind::ReadFrameAddress(
"Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
cfa_reg_contents);
- cfa_reg_contents = LLDB_INVALID_ADDRESS;
return false;
}
address = cfa_reg_contents + fa.GetOffset();
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index d97a814..f27004c 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -61,8 +61,9 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
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), m_frame_code_addr(pc), m_sc(), m_flags(),
- m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid),
+ 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_behaves_like_zeroth_frame(behaves_like_zeroth_frame),
m_variable_list_sp(), m_variable_list_value_objects(),
@@ -71,7 +72,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
// recursive functions properly aren't confused with one another on a history
// stack.
if (IsHistorical() && !m_cfa_is_valid) {
- m_id.SetCFA(m_frame_index);
+ m_id.SetCFA(m_frame_index, thread_sp->GetProcess().get());
}
if (sc_ptr != nullptr) {
@@ -87,7 +88,8 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
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(reg_context_sp), m_id(pc, cfa, nullptr),
+ m_reg_context_sp(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(true),
m_stack_frame_kind(StackFrame::Kind::Regular),
@@ -115,7 +117,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx,
m_concrete_frame_index(unwind_frame_index),
m_reg_context_sp(reg_context_sp),
m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa,
- nullptr),
+ 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),
@@ -264,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();
@@ -438,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());
}
}
@@ -1225,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);
@@ -1762,11 +1767,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;
}
@@ -1792,8 +1795,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) {
@@ -1803,7 +1805,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg,
return ValueObjectSP();
}
-}
+} // namespace
lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg,
int64_t offset) {
@@ -1995,7 +1997,9 @@ void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) {
std::lock_guard<std::recursive_mutex> guard(m_mutex);
assert(GetStackID() ==
curr_frame.GetStackID()); // TODO: remove this after some testing
- m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value
+ m_id.SetPC(
+ curr_frame.m_id.GetPC(),
+ curr_frame.CalculateProcess().get()); // Update the Stack ID PC value
assert(GetThread() == curr_frame.GetThread());
m_frame_index = curr_frame.m_frame_index;
m_concrete_frame_index = curr_frame.m_concrete_frame_index;
diff --git a/lldb/source/Target/StackFrameRecognizer.cpp b/lldb/source/Target/StackFrameRecognizer.cpp
index 9d5116c..d9dbed8 100644
--- a/lldb/source/Target/StackFrameRecognizer.cpp
+++ b/lldb/source/Target/StackFrameRecognizer.cpp
@@ -152,7 +152,7 @@ StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) {
Address start_addr = symbol->GetAddress();
Address current_addr = frame->GetFrameCodeAddress();
- for (auto entry : m_recognizers) {
+ for (const auto &entry : m_recognizers) {
if (!entry.enabled)
continue;
diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp
index 410d5b7..b179597 100644
--- a/lldb/source/Target/StackID.cpp
+++ b/lldb/source/Target/StackID.cpp
@@ -10,10 +10,28 @@
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Process.h"
#include "lldb/Utility/Stream.h"
using namespace lldb_private;
+StackID::StackID(lldb::addr_t pc, lldb::addr_t cfa,
+ SymbolContextScope *symbol_scope, Process *process)
+ : m_pc(pc), m_cfa(cfa), m_symbol_scope(symbol_scope) {
+ if (process) {
+ m_pc = process->FixCodeAddress(m_pc);
+ m_cfa = process->FixDataAddress(m_cfa);
+ }
+}
+
+void StackID::SetPC(lldb::addr_t pc, Process *process) {
+ m_pc = process ? process->FixCodeAddress(pc) : pc;
+}
+
+void StackID::SetCFA(lldb::addr_t cfa, Process *process) {
+ m_cfa = process ? process->FixDataAddress(cfa) : cfa;
+}
+
void StackID::Dump(Stream *s) {
s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64
", symbol_scope = %p",
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 4f39f60..fa98c24 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -558,10 +558,11 @@ BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal,
BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal,
bool hardware) {
- SearchFilterSP filter_sp(
- new SearchFilterForUnconstrainedSearches(shared_from_this()));
- BreakpointResolverSP resolver_sp(
- new BreakpointResolverAddress(nullptr, addr));
+ SearchFilterSP filter_sp =
+ std::make_shared<SearchFilterForUnconstrainedSearches>(
+ shared_from_this());
+ BreakpointResolverSP resolver_sp =
+ std::make_shared<BreakpointResolverAddress>(nullptr, addr);
return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false);
}
@@ -569,10 +570,12 @@ lldb::BreakpointSP
Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal,
const FileSpec &file_spec,
bool request_hardware) {
- SearchFilterSP filter_sp(
- new SearchFilterForUnconstrainedSearches(shared_from_this()));
- BreakpointResolverSP resolver_sp(new BreakpointResolverAddress(
- nullptr, file_addr, file_spec));
+ SearchFilterSP filter_sp =
+ std::make_shared<SearchFilterForUnconstrainedSearches>(
+ shared_from_this());
+ BreakpointResolverSP resolver_sp =
+ std::make_shared<BreakpointResolverAddress>(nullptr, file_addr,
+ file_spec);
return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware,
false);
}
@@ -581,7 +584,8 @@ BreakpointSP Target::CreateBreakpoint(
const FileSpecList *containingModules,
const FileSpecList *containingSourceFiles, const char *func_name,
FunctionNameType func_name_type_mask, LanguageType language,
- lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) {
+ lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue,
+ bool internal, bool hardware) {
BreakpointSP bp_sp;
if (func_name) {
SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList(
@@ -594,7 +598,7 @@ BreakpointSP Target::CreateBreakpoint(
BreakpointResolverSP resolver_sp(new BreakpointResolverName(
nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact,
- offset, skip_prologue));
+ offset, offset_is_insn_count, skip_prologue));
bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true);
}
return bp_sp;
@@ -2996,6 +3000,38 @@ lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) {
return arch_plugin ? arch_plugin->GetBreakableLoadAddress(addr, *this) : addr;
}
+llvm::Expected<lldb::DisassemblerSP>
+Target::ReadInstructions(const Address &start_addr, uint32_t count,
+ const char *flavor_string) {
+ DataBufferHeap data(GetArchitecture().GetMaximumOpcodeByteSize() * count, 0);
+ bool force_live_memory = true;
+ lldb_private::Status error;
+ lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
+ const size_t bytes_read =
+ ReadMemory(start_addr, data.GetBytes(), data.GetByteSize(), error,
+ force_live_memory, &load_addr);
+
+ if (error.Fail())
+ return llvm::createStringError(
+ error.AsCString("Target::ReadInstructions failed to read memory at %s"),
+ start_addr.GetLoadAddress(this));
+
+ const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS;
+ if (!flavor_string || flavor_string[0] == '\0') {
+ // FIXME - we don't have the mechanism in place to do per-architecture
+ // settings. But since we know that for now we only support flavors on
+ // x86 & x86_64,
+ const llvm::Triple::ArchType arch = GetArchitecture().GetTriple().getArch();
+ if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64)
+ flavor_string = GetDisassemblyFlavor();
+ }
+
+ return Disassembler::DisassembleBytes(
+ GetArchitecture(), nullptr, flavor_string, GetDisassemblyCPU(),
+ GetDisassemblyFeatures(), start_addr, data.GetBytes(), bytes_read, count,
+ data_from_file);
+}
+
SourceManager &Target::GetSourceManager() {
if (!m_source_manager_up)
m_source_manager_up = std::make_unique<SourceManager>(shared_from_this());
diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp
index 7c71aaa..8f14cf6 100644
--- a/lldb/source/Utility/ArchSpec.cpp
+++ b/lldb/source/Utility/ArchSpec.cpp
@@ -1439,6 +1439,10 @@ bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) {
return lhs.GetCore() == rhs.GetCore();
}
+bool lldb_private::operator!=(const ArchSpec &lhs, const ArchSpec &rhs) {
+ return !(lhs == rhs);
+}
+
bool ArchSpec::IsFullySpecifiedTriple() const {
if (!TripleOSWasSpecified())
return false;
diff --git a/lldb/source/Utility/Scalar.cpp b/lldb/source/Utility/Scalar.cpp
index f07a9f3..7fbe46d 100644
--- a/lldb/source/Utility/Scalar.cpp
+++ b/lldb/source/Utility/Scalar.cpp
@@ -82,7 +82,7 @@ Scalar::Type Scalar::PromoteToMaxType(Scalar &lhs, Scalar &rhs) {
return Scalar::e_void;
}
-bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const {
+bool Scalar::GetData(DataExtractor &data) const {
size_t byte_size = GetByteSize();
if (byte_size == 0) {
data.Clear();
@@ -90,27 +90,57 @@ bool Scalar::GetData(DataExtractor &data, size_t limit_byte_size) const {
}
auto buffer_up = std::make_unique<DataBufferHeap>(byte_size, 0);
GetBytes(buffer_up->GetData());
- lldb::offset_t offset = 0;
-
- if (limit_byte_size < byte_size) {
- if (endian::InlHostByteOrder() == eByteOrderLittle) {
- // On little endian systems if we want fewer bytes from the current
- // type we just specify fewer bytes since the LSByte is first...
- byte_size = limit_byte_size;
- } else if (endian::InlHostByteOrder() == eByteOrderBig) {
- // On big endian systems if we want fewer bytes from the current type
- // have to advance our initial byte pointer and trim down the number of
- // bytes since the MSByte is first
- offset = byte_size - limit_byte_size;
- byte_size = limit_byte_size;
+ data.SetData(std::move(buffer_up), 0, byte_size);
+ data.SetByteOrder(endian::InlHostByteOrder());
+ return true;
+}
+
+bool Scalar::GetData(DataExtractor &data, size_t result_byte_size) const {
+ size_t byte_size = GetByteSize();
+ if (byte_size == 0 || result_byte_size == 0) {
+ data.Clear();
+ return false;
+ }
+
+ if (endian::InlHostByteOrder() == lldb::eByteOrderBig) {
+ // On big endian systems if we want fewer bytes from the current type
+ // we have to advance our initial byte pointer since the MSByte is
+ // first.
+ if (result_byte_size <= byte_size) {
+ auto buffer_up = std::make_unique<DataBufferHeap>(byte_size, 0);
+ GetBytes(buffer_up->GetData());
+ auto offset = byte_size - result_byte_size;
+ data.SetData(std::move(buffer_up), offset, result_byte_size);
+ data.SetByteOrder(endian::InlHostByteOrder());
+ } else {
+ // Extend created buffer size and insert the data bytes with an offset
+ auto buffer_up = std::make_unique<DataBufferHeap>(result_byte_size, 0);
+ auto offset = result_byte_size - byte_size;
+ GetBytes(buffer_up->GetBytes() + offset, byte_size);
+ data.SetData(std::move(buffer_up), 0, result_byte_size);
+ data.SetByteOrder(endian::InlHostByteOrder());
}
+ return true;
}
- data.SetData(std::move(buffer_up), offset, byte_size);
+ // On little endian systems MSBytes get trimmed or extended automatically by
+ // size.
+ if (byte_size < result_byte_size)
+ byte_size = result_byte_size;
+ auto buffer_up = std::make_unique<DataBufferHeap>(byte_size, 0);
+ GetBytes(buffer_up->GetData());
+ data.SetData(std::move(buffer_up), 0, result_byte_size);
data.SetByteOrder(endian::InlHostByteOrder());
+
return true;
}
+void Scalar::GetBytes(uint8_t *storage, size_t size) const {
+ assert(size >= GetByteSize());
+ llvm::MutableArrayRef<uint8_t> storage_ref(storage, size);
+ GetBytes(storage_ref);
+}
+
void Scalar::GetBytes(llvm::MutableArrayRef<uint8_t> storage) const {
assert(storage.size() >= GetByteSize());
diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp
index eb2047e..2040791 100644
--- a/lldb/source/Utility/XcodeSDK.cpp
+++ b/lldb/source/Utility/XcodeSDK.cpp
@@ -243,29 +243,6 @@ bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type,
return false;
}
-bool XcodeSDK::SupportsSwift() const {
- XcodeSDK::Info info = Parse();
- switch (info.type) {
- case Type::MacOSX:
- return info.version.empty() || info.version >= llvm::VersionTuple(10, 10);
- case Type::iPhoneOS:
- case Type::iPhoneSimulator:
- return info.version.empty() || info.version >= llvm::VersionTuple(8);
- case Type::AppleTVSimulator:
- case Type::AppleTVOS:
- return info.version.empty() || info.version >= llvm::VersionTuple(9);
- case Type::WatchSimulator:
- case Type::watchOS:
- return info.version.empty() || info.version >= llvm::VersionTuple(2);
- case Type::XROS:
- case Type::XRSimulator:
- case Type::Linux:
- return true;
- default:
- return false;
- }
-}
-
bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type,
const FileSpec &sdk_path) {
ConstString last_path_component = sdk_path.GetFilename();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index b1cd824..7056466 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 6f28434..c6cf41e 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 eaefaf4..0b2288a 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 eac41fa..8c4f7fd 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/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index 3878442..38b9f77 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -3590,6 +3590,13 @@ lldb::ValueObjectSP ValueObject::CreateValueObjectFromAPFloat(
return CreateValueObjectFromAPInt(target, v.bitcastToAPInt(), type, name);
}
+lldb::ValueObjectSP ValueObject::CreateValueObjectFromScalar(
+ lldb::TargetSP target, Scalar &s, CompilerType type, llvm::StringRef name) {
+ ExecutionContext exe_ctx(target.get(), false);
+ return ValueObjectConstResult::Create(exe_ctx.GetBestExecutionContextScope(),
+ type, s, ConstString(name));
+}
+
lldb::ValueObjectSP
ValueObject::CreateValueObjectFromBool(lldb::TargetSP target, bool value,
llvm::StringRef name) {
diff --git a/lldb/source/ValueObject/ValueObjectConstResult.cpp b/lldb/source/ValueObject/ValueObjectConstResult.cpp
index 7747496..10a6297 100644
--- a/lldb/source/ValueObject/ValueObjectConstResult.cpp
+++ b/lldb/source/ValueObject/ValueObjectConstResult.cpp
@@ -105,6 +105,16 @@ ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
->GetSP();
}
+ValueObjectSP ValueObjectConstResult::Create(ExecutionContextScope *exe_scope,
+ const CompilerType &compiler_type,
+ Scalar &scalar, ConstString name,
+ Module *module) {
+ auto manager_sp = ValueObjectManager::Create();
+ return (new ValueObjectConstResult(exe_scope, *manager_sp, compiler_type,
+ scalar, name, module))
+ ->GetSP();
+}
+
ValueObjectConstResult::ValueObjectConstResult(
ExecutionContextScope *exe_scope, ValueObjectManager &manager,
const CompilerType &compiler_type, ConstString name,
@@ -193,6 +203,23 @@ ValueObjectConstResult::ValueObjectConstResult(ExecutionContextScope *exe_scope,
m_error = m_value.GetValueAsData(&exe_ctx, m_data, module);
}
+ValueObjectConstResult::ValueObjectConstResult(
+ ExecutionContextScope *exe_scope, ValueObjectManager &manager,
+ const CompilerType &compiler_type, const Scalar &scalar, ConstString name,
+ Module *module)
+ : ValueObject(exe_scope, manager), m_impl(this) {
+ m_value = Value(scalar);
+ m_value.SetCompilerType(compiler_type);
+ m_value.SetValueType(Value::ValueType::Scalar);
+ m_name = name;
+ ExecutionContext exe_ctx;
+ exe_scope->CalculateExecutionContext(exe_ctx);
+ m_error = m_value.GetValueAsData(&exe_ctx, m_data, module);
+ SetIsConstant();
+ SetValueIsValid(true);
+ SetAddressTypeOfChildren(eAddressTypeLoad);
+}
+
ValueObjectConstResult::~ValueObjectConstResult() = default;
CompilerType ValueObjectConstResult::GetCompilerTypeImpl() {
diff --git a/lldb/test/API/arm/thumb-function-addr/Makefile b/lldb/test/API/arm/thumb-function-addr/Makefile
new file mode 100644
index 0000000..1049594
--- /dev/null
+++ b/lldb/test/API/arm/thumb-function-addr/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/arm/thumb-function-addr/TestThumbFunctionAddr.py b/lldb/test/API/arm/thumb-function-addr/TestThumbFunctionAddr.py
new file mode 100644
index 0000000..d08099f
--- /dev/null
+++ b/lldb/test/API/arm/thumb-function-addr/TestThumbFunctionAddr.py
@@ -0,0 +1,67 @@
+"""
+Test that addresses of functions compiled for Arm Thumb include the Thumb mode
+bit (bit 0 of the address) when resolved and used in expressions.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestThumbFunctionAddr(TestBase):
+ def do_thumb_function_test(self, language):
+ self.build(dictionary={"CFLAGS_EXTRAS": f"-x {language} -mthumb"})
+
+ exe = self.getBuildArtifact("a.out")
+ line = line_number("main.c", "// Set break point at this line.")
+ self.runCmd("target create %s" % exe)
+ bpid = lldbutil.run_break_set_by_file_and_line(self, "main.c", line)
+
+ self.runCmd("run")
+ self.assertIsNotNone(
+ lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(), bpid),
+ "Process is not stopped at breakpoint",
+ )
+
+ # The compiler set this, so the mode bit will be included here.
+ a_function_addr_var = (
+ self.thread().GetFrameAtIndex(0).FindVariable("a_function_addr")
+ )
+ self.assertTrue(a_function_addr_var.IsValid())
+ a_function_addr = a_function_addr_var.GetValueAsUnsigned()
+ self.assertTrue(a_function_addr & 1)
+
+ self.expect("p/x a_function_addr", substrs=[f"0x{a_function_addr:08x}"])
+ # If lldb did not pay attention to the mode bit this would SIGILL trying
+ # to execute Thumb encodings in Arm mode.
+ self.expect("expression -- a_function()", substrs=["= 123"])
+
+ # We cannot call GetCallableLoadAdress via. the API, so we expect this
+ # to not have the bit set as it's treating it as a non-function symbol.
+ found_function = self.target().FindFunctions("a_function")[0]
+ self.assertTrue(found_function.IsValid())
+ found_function = found_function.GetFunction()
+ self.assertTrue(found_function.IsValid())
+ found_function_addr = found_function.GetStartAddress()
+ a_function_load_addr = found_function_addr.GetLoadAddress(self.target())
+ self.assertEqual(a_function_load_addr, a_function_addr & ~1)
+
+ # image lookup should not include the mode bit.
+ a_function_file_addr = found_function_addr.GetFileAddress()
+ self.expect(
+ "image lookup -n a_function", substrs=[f"0x{a_function_file_addr:08x}"]
+ )
+
+ # This test is run for C and C++ because the two will take different paths
+ # trying to resolve the function's address.
+
+ @skipIf(archs=no_match(["arm$"]))
+ @skipIf(archs=["arm64"])
+ def test_function_addr_c(self):
+ self.do_thumb_function_test("c")
+
+ @skipIf(archs=no_match(["arm$"]))
+ @skipIf(archs=["arm64"])
+ def test_function_addr_cpp(self):
+ self.do_thumb_function_test("c++")
diff --git a/lldb/test/API/arm/thumb-function-addr/main.c b/lldb/test/API/arm/thumb-function-addr/main.c
new file mode 100644
index 0000000..f3e01b7
--- /dev/null
+++ b/lldb/test/API/arm/thumb-function-addr/main.c
@@ -0,0 +1,9 @@
+#include <stdint.h>
+
+int a_function() { return 123; }
+
+int main() {
+ const uintptr_t a_function_addr = (uintptr_t)a_function;
+ // Set break point at this line.
+ return a_function();
+}
diff --git a/lldb/test/API/commands/expression/TestRegisterExpressionEndian.py b/lldb/test/API/commands/expression/TestRegisterExpressionEndian.py
new file mode 100644
index 0000000..d6de873
--- /dev/null
+++ b/lldb/test/API/commands/expression/TestRegisterExpressionEndian.py
@@ -0,0 +1,128 @@
+""" Check that registers written to memory for expression evaluation are
+ written using the target's endian not the host's.
+"""
+
+from enum import Enum
+from textwrap import dedent
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+
+class Endian(Enum):
+ BIG = 0
+ LITTLE = 1
+
+
+class Responder(MockGDBServerResponder):
+ def __init__(self, doc, endian):
+ super().__init__()
+ self.target_xml = doc
+ self.endian = endian
+
+ def qXferRead(self, obj, annex, offset, length):
+ if annex == "target.xml":
+ return self.target_xml, False
+ return (None,)
+
+ def readRegister(self, regnum):
+ return "E01"
+
+ def readRegisters(self):
+ # 64 bit pc value.
+ data = ["00", "00", "00", "00", "00", "00", "12", "34"]
+ if self.endian == Endian.LITTLE:
+ data.reverse()
+ return "".join(data)
+
+
+class TestXMLRegisterFlags(GDBRemoteTestBase):
+ def do_endian_test(self, endian):
+ architecture, pc_reg_name, yaml_file, data, machine = {
+ Endian.BIG: ("s390x", "pswa", "s390x.yaml", "ELFDATA2MSB", "EM_S390"),
+ Endian.LITTLE: (
+ "aarch64",
+ "pc",
+ "aarch64.yaml",
+ "ELFDATA2LSB",
+ "EM_AARCH64",
+ ),
+ }[endian]
+
+ self.server.responder = Responder(
+ dedent(
+ f"""\
+ <?xml version="1.0"?>
+ <target version="1.0">
+ <architecture>{architecture}</architecture>
+ <feature>
+ <reg name="{pc_reg_name}" bitsize="64"/>
+ </feature>
+ </target>"""
+ ),
+ endian,
+ )
+
+ # We need to have a program file, so that we have a full type system,
+ # so that we can do the casts later.
+ obj_path = self.getBuildArtifact("main.o")
+ yaml_path = self.getBuildArtifact(yaml_file)
+ with open(yaml_path, "w") as f:
+ f.write(
+ dedent(
+ f"""\
+ --- !ELF
+ FileHeader:
+ Class: ELFCLASS64
+ Data: {data}
+ Type: ET_REL
+ Machine: {machine}
+ ...
+ """
+ )
+ )
+ self.yaml2obj(yaml_path, obj_path)
+ target = self.dbg.CreateTarget(obj_path)
+
+ process = self.connect(target)
+ lldbutil.expect_state_changes(
+ self, self.dbg.GetListener(), process, [lldb.eStateStopped]
+ )
+
+ # If expressions convert register values into target endian, the
+ # result of register read, expr and casts should be the same.
+ pc_value = "0x0000000000001234"
+ self.expect(
+ "register read pc",
+ substrs=[pc_value],
+ )
+ self.expect("expr --format hex -- $pc", substrs=[pc_value])
+
+ pc = (
+ process.thread[0]
+ .frame[0]
+ .GetRegisters()
+ .GetValueAtIndex(0)
+ .GetChildMemberWithName("pc")
+ )
+ ull = target.FindTypes("unsigned long long").GetTypeAtIndex(0)
+ pc_ull = pc.Cast(ull)
+
+ self.assertEqual(pc.GetValue(), pc_ull.GetValue())
+ self.assertEqual(pc.GetValueAsAddress(), pc_ull.GetValueAsAddress())
+ self.assertEqual(pc.GetValueAsSigned(), pc_ull.GetValueAsSigned())
+ self.assertEqual(pc.GetValueAsUnsigned(), pc_ull.GetValueAsUnsigned())
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ @skipIfLLVMTargetMissing("AArch64")
+ def test_little_endian_target(self):
+ self.do_endian_test(Endian.LITTLE)
+
+ @skipIfXmlSupportMissing
+ @skipIfRemote
+ @skipIfLLVMTargetMissing("SystemZ")
+ def test_big_endian_target(self):
+ self.do_endian_test(Endian.BIG)
diff --git a/lldb/test/API/commands/expression/import-std-module/queue/TestQueueFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/queue/TestQueueFromStdModule.py
index b08a538..95aaa8e 100644
--- a/lldb/test/API/commands/expression/import-std-module/queue/TestQueueFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/queue/TestQueueFromStdModule.py
@@ -15,6 +15,10 @@ class TestQueue(TestBase):
compiler_version=[">", "16.0"],
bugnumber="https://github.com/llvm/llvm-project/issues/68968",
)
+ @skipIf(
+ compiler="clang",
+ compiler_version=["<", "17.0"],
+ )
def test(self):
self.build()
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 0f56057..f47e862 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 a9a3612..03ad3e8 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 0000000..167899b
--- /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 38c7213..28eba4f 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 0000000..99998b2
--- /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 0000000..431ec28
--- /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 0000000..c9bd8af
--- /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/settings/TestSettings.py b/lldb/test/API/commands/settings/TestSettings.py
index bc86494..95b07f8 100644
--- a/lldb/test/API/commands/settings/TestSettings.py
+++ b/lldb/test/API/commands/settings/TestSettings.py
@@ -972,6 +972,123 @@ class SettingsCommandTestCase(TestBase):
# A known option should fail if its argument is invalid.
self.expect("settings set auto-confirm bogus", error=True)
+ def test_settings_show_defaults(self):
+ # boolean
+ self.expect(
+ "settings show --defaults auto-one-line-summaries",
+ matching=False,
+ substrs=["(default: true)"],
+ )
+ self.runCmd("settings set auto-one-line-summaries false")
+ self.expect(
+ "settings show --defaults auto-one-line-summaries",
+ substrs=["= false (default: true)"],
+ )
+ # unsigned
+ self.expect(
+ "settings show --defaults stop-line-count-before",
+ matching=False,
+ patterns=[r"\(default: \d+\)"],
+ )
+ self.runCmd("settings set stop-line-count-before 99")
+ self.expect(
+ "settings show --defaults stop-line-count-before",
+ patterns=[r"= 99 \(default: \d+\)"],
+ )
+ # string
+ self.expect(
+ "settings show --defaults prompt",
+ matching=False,
+ patterns=[r'\(default: ".+"\)'],
+ )
+ self.runCmd("settings set prompt '<LlDb> '")
+ self.expect(
+ "settings show --defaults prompt",
+ patterns=[r'= "<LlDb> " \(default: ".+"\)'],
+ )
+ # enum
+ self.expect(
+ "settings show --defaults stop-disassembly-display",
+ matching=False,
+ patterns=[r"\(default: .+\)"],
+ )
+ self.runCmd("settings set stop-disassembly-display no-source")
+ self.expect(
+ "settings show --defaults stop-disassembly-display",
+ patterns=[r"= no-source \(default: .+\)"],
+ )
+ # regex
+ self.expect(
+ "settings show --defaults target.process.thread.step-avoid-regexp",
+ matching=False,
+ patterns=[r"\(default: .+\)"],
+ )
+ self.runCmd("settings set target.process.thread.step-avoid-regexp dotstar")
+ self.expect(
+ "settings show --defaults target.process.thread.step-avoid-regexp",
+ patterns=[r"= dotstar \(default: .+\)"],
+ )
+ # format-string
+ self.expect(
+ "settings show --defaults disassembly-format",
+ matching=False,
+ patterns=[r'\(default: ".+"\)'],
+ )
+ self.runCmd("settings set disassembly-format dollar")
+ self.expect(
+ "settings show --defaults disassembly-format",
+ patterns=[r'= "dollar" \(default: ".+"\)'],
+ )
+ # arrays
+ self.expect(
+ "settings show --defaults target.unset-env-vars",
+ matching=False,
+ substrs=["(default: empty)"],
+ )
+ self.runCmd("settings set target.unset-env-vars PATH")
+ self.expect(
+ "settings show --defaults target.unset-env-vars",
+ substrs=["(default: empty)", '[0]: "PATH"'],
+ )
+ # dictionaries
+ self.runCmd("settings clear target.env-vars")
+ self.expect(
+ "settings show --defaults target.env-vars",
+ matching=False,
+ substrs=["(default: empty)"],
+ )
+ self.runCmd("settings set target.env-vars THING=value")
+ self.expect(
+ "settings show --defaults target.env-vars",
+ substrs=["(default: empty)", "THING=value"],
+ )
+ pwd = os.getcwd()
+ # file list
+ self.expect(
+ "settings show --defaults target.exec-search-paths",
+ matching=False,
+ substrs=["(default: empty)"],
+ )
+ self.runCmd(f"settings set target.exec-search-paths {pwd}")
+ self.expect(
+ "settings show --defaults target.exec-search-paths",
+ substrs=["(default: empty)", f"[0]: {pwd}"],
+ )
+ # path map
+ self.expect(
+ "settings show --defaults target.source-map",
+ matching=False,
+ substrs=["(default: empty)"],
+ )
+ self.runCmd(f"settings set target.source-map /abc {pwd}")
+ self.expect(
+ "settings show --defaults target.source-map",
+ patterns=[
+ r"\(default: empty\)",
+ rf'\[0\] "[/\\]abc" -> "{re.escape(pwd)}"',
+ ],
+ )
+
def get_setting_json(self, setting_path=None):
settings_data = self.dbg.GetSetting(setting_path)
stream = lldb.SBStream()
diff --git a/lldb/test/API/functionalities/asan/TestMemoryHistory.py b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
index 1568140..66f6e3e 100644
--- a/lldb/test/API/functionalities/asan/TestMemoryHistory.py
+++ b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
@@ -41,18 +41,16 @@ class MemoryHistoryTestCase(TestBase):
self.line_free = line_number("main.c", "// free line")
self.line_breakpoint = line_number("main.c", "// break line")
- # Test line numbers: rdar://126237493
- # for libsanitizers and remove `skip_line_numbers` parameter
- def check_traces(self, skip_line_numbers=False):
+ def check_traces(self):
self.expect(
"memory history 'pointer'",
substrs=[
"Memory deallocated by Thread",
"a.out`f2",
- "main.c" if skip_line_numbers else f"main.c:{self.line_free}",
+ f"main.c:{self.line_free}",
"Memory allocated by Thread",
"a.out`f1",
- "main.c" if skip_line_numbers else f"main.c:{self.line_malloc}",
+ f"main.c:{self.line_malloc}",
],
)
@@ -76,7 +74,7 @@ class MemoryHistoryTestCase(TestBase):
self.runCmd("env SanitizersAllocationTraces=all")
self.run_to_breakpoint(target)
- self.check_traces(skip_line_numbers=True)
+ self.check_traces()
def libsanitizers_asan_tests(self):
target = self.createTestTarget()
@@ -84,7 +82,7 @@ class MemoryHistoryTestCase(TestBase):
self.runCmd("env SanitizersAddress=1 MallocSanitizerZone=1")
self.run_to_breakpoint(target)
- self.check_traces(skip_line_numbers=True)
+ self.check_traces()
self.runCmd("continue")
@@ -94,7 +92,7 @@ class MemoryHistoryTestCase(TestBase):
"Process should be stopped due to ASan report",
substrs=["stopped", "stop reason = Use of deallocated memory"],
)
- self.check_traces(skip_line_numbers=True)
+ self.check_traces()
# do the same using SB API
process = self.dbg.GetSelectedTarget().process
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 f135da7..9ba10575 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 49c0c81..fec20bae 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
@@ -217,9 +217,6 @@ class StdStringDataFormatterTestCase(TestBase):
self.build(dictionary={"USE_LIBCPP": 1})
self.do_test_summary_unavailable()
- @expectedFailureAll(
- bugnumber="libstdc++ std::string summary provider doesn't output a user-friendly message for invalid strings."
- )
@add_test_categories(["libstdcxx"])
def test_unavailable_summary_libstdcxx(self):
self.build(dictionary={"USE_LIBSTDCPP": 1})
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 f108118..5943b35 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 c8d9c2e3..f27fc2e 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 cf431e5..b19c051 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 e623c3a..1e25ac9 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 3d174a9..bd840aa 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 0000000..3d4bcd22
--- /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 0000000..f107efb
--- /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 0000000..c38742c
--- /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 0000000..cd9f08a
--- /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 0000000..c01e2b2
--- /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 0000000..502ab15
--- /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 0000000..0b2a60e
--- /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 0000000..691180b
--- /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 0000000..f85b8a7
--- /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/process_crash_info/TestProcessCrashInfo.py b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
index af05c2f..c143da2 100644
--- a/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
+++ b/lldb/test/API/functionalities/process_crash_info/TestProcessCrashInfo.py
@@ -22,6 +22,15 @@ class PlatformProcessCrashInfoTestCase(TestBase):
self.runCmd("settings clear auto-confirm")
TestBase.tearDown(self)
+ def containsLibmallocError(self, output):
+ for error in [
+ "pointer being freed was not allocated",
+ "not an allocated block",
+ ]:
+ if error in output:
+ return True
+ return False
+
@skipIfAsan # The test process intentionally double-frees.
@skipUnlessDarwin
def test_cli(self):
@@ -33,15 +42,15 @@ class PlatformProcessCrashInfoTestCase(TestBase):
self.expect("process launch", patterns=["Process .* launched: .*a.out"])
- self.expect(
- "process status --verbose",
- patterns=[
- "Extended Crash Information",
- "Crash-Info Annotations",
- "pointer being freed was not allocated",
- ],
+ result = lldb.SBCommandReturnObject()
+ self.dbg.GetCommandInterpreter().HandleCommand(
+ "process status --verbose", result
)
+ self.assertIn("Extended Crash Information", result.GetOutput())
+ self.assertIn("Crash-Info Annotations", result.GetOutput())
+ self.assertTrue(self.containsLibmallocError(result.GetOutput()))
+
@skipIfAsan # The test process intentionally hits a memory bug.
@skipUnlessDarwin
def test_api(self):
@@ -67,7 +76,7 @@ class PlatformProcessCrashInfoTestCase(TestBase):
self.assertTrue(crash_info.IsValid())
- self.assertIn("pointer being freed was not allocated", stream.GetData())
+ self.assertTrue(self.containsLibmallocError(stream.GetData()))
# dyld leaves permanent crash_info records when testing on device.
@skipIfDarwinEmbedded
diff --git a/lldb/test/API/functionalities/statusline/TestStatusline.py b/lldb/test/API/functionalities/statusline/TestStatusline.py
index 33cd797..ca376cc 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/state/TestThreadStates.py b/lldb/test/API/functionalities/thread/state/TestThreadStates.py
index 4dbe230..6eeff07 100644
--- a/lldb/test/API/functionalities/thread/state/TestThreadStates.py
+++ b/lldb/test/API/functionalities/thread/state/TestThreadStates.py
@@ -30,7 +30,6 @@ class ThreadStateTestCase(TestBase):
@expectedFailureAll(
oslist=lldbplatformutil.getDarwinOSTriples(), bugnumber="llvm.org/pr23669"
)
- @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24660")
def test_state_after_continue(self):
"""Test thread state after continue."""
self.build()
diff --git a/lldb/test/API/lang/c/step-target/TestStepTarget.py b/lldb/test/API/lang/c/step-target/TestStepTarget.py
index 2da0a78..e5bd64d 100644
--- a/lldb/test/API/lang/c/step-target/TestStepTarget.py
+++ b/lldb/test/API/lang/c/step-target/TestStepTarget.py
@@ -83,14 +83,16 @@ class TestStepTarget(TestBase):
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr32343")
def test_with_command_and_block(self):
"""Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms."""
+ self.do_command_and_block()
+ self.do_command_and_block(True)
+ def do_command_and_block(self, use_regexp_step=False):
thread = self.get_to_start()
- result = lldb.SBCommandReturnObject()
- self.dbg.GetCommandInterpreter().HandleCommand(
- 'thread step-in -t "lotsOfArgs" -e block', result
- )
- self.assertTrue(result.Succeeded(), "thread step-in command succeeded.")
+ if use_regexp_step:
+ self.expect("s lotsOfArgs")
+ else:
+ self.expect('thread step-in -t "lotsOfArgs" -e block')
frame = thread.frames[0]
self.assertEqual(frame.name, "lotsOfArgs", "Stepped to lotsOfArgs.")
@@ -98,14 +100,16 @@ class TestStepTarget(TestBase):
@expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr32343")
def test_with_command_and_block_and_bad_name(self):
"""Test stepping over vrs. hitting breakpoints & subsequent stepping in various forms."""
+ self.do_with_command_and_block_and_bad_name()
+ self.do_with_command_and_block_and_bad_name(True)
+ def do_with_command_and_block_and_bad_name(self, use_regexp_step=False):
thread = self.get_to_start()
- result = lldb.SBCommandReturnObject()
- self.dbg.GetCommandInterpreter().HandleCommand(
- 'thread step-in -t "lotsOfArgsssss" -e block', result
- )
- self.assertTrue(result.Succeeded(), "thread step-in command succeeded.")
+ if use_regexp_step:
+ self.expect("s lotsOfArgsssss")
+ else:
+ self.expect('thread step-in -t "lotsOfArgsssss" -e block')
frame = thread.frames[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 02c34b3..70d7fd0 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
@@ -31,3 +31,10 @@ 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"]
+ )
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 ad148ce..1a08817 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
@@ -1,3 +1,13 @@
#include "lib.h"
+#include <cstdio>
+
int Foo::method() { return -72; }
+
+Foo::Foo() { std::puts(__func__); }
+
+Foo::~Foo() { std::puts(__func__); }
+
+Bar::Bar() { std::puts(__func__); }
+
+Bar::~Bar() { 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 9568db2..5ec2279 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,6 +3,13 @@
struct Foo {
int method();
+ Foo();
+ ~Foo();
+};
+
+struct Bar {
+ [[gnu::abi_tag("Ctor")]] Bar();
+ [[gnu::abi_tag("Dtor")]] ~Bar();
};
#endif // LIB_H_IN
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 2fddb2b..4d6bece 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
@@ -2,5 +2,6 @@
int main() {
Foo f;
+ Bar b;
return f.method();
}
diff --git a/lldb/test/API/lang/cpp/lambdas/TestLambdas.py b/lldb/test/API/lang/cpp/lambdas/TestLambdas.py
index c8308c1..112f375 100644
--- a/lldb/test/API/lang/cpp/lambdas/TestLambdas.py
+++ b/lldb/test/API/lang/cpp/lambdas/TestLambdas.py
@@ -1,4 +1,12 @@
from lldbsuite.test import lldbinline
from lldbsuite.test import decorators
-lldbinline.MakeInlineTest(__file__, globals())
+lldbinline.MakeInlineTest(
+ __file__,
+ globals(),
+ [
+ decorators.expectedFailureAll(
+ bugnumber="https://github.com/llvm/llvm-project/issues/149477"
+ )
+ ],
+)
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 8efa53b..d8a729b 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/lang/objc/failing-description/Makefile b/lldb/test/API/lang/objc/failing-description/Makefile
new file mode 100644
index 0000000..c8d46cd4
--- /dev/null
+++ b/lldb/test/API/lang/objc/failing-description/Makefile
@@ -0,0 +1,3 @@
+OBJC_SOURCES := main.m
+LD_EXTRAS := -lobjc -framework Foundation
+include Makefile.rules
diff --git a/lldb/test/API/lang/objc/failing-description/TestObjCFailingDescription.py b/lldb/test/API/lang/objc/failing-description/TestObjCFailingDescription.py
new file mode 100644
index 0000000..a06ff57
--- /dev/null
+++ b/lldb/test/API/lang/objc/failing-description/TestObjCFailingDescription.py
@@ -0,0 +1,17 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
+ self.expect(
+ "expr -O -- bad", substrs=["error:", "expression interrupted", "(Bad *) 0x"]
+ )
+ self.expect(
+ "dwim-print -O -- bad",
+ substrs=["error:", "expression interrupted", "_lookHere = NO"],
+ )
diff --git a/lldb/test/API/lang/objc/failing-description/main.m b/lldb/test/API/lang/objc/failing-description/main.m
new file mode 100644
index 0000000..7416e0a
--- /dev/null
+++ b/lldb/test/API/lang/objc/failing-description/main.m
@@ -0,0 +1,21 @@
+#import <Foundation/Foundation.h>
+
+@interface Bad : NSObject
+@end
+
+@implementation Bad {
+ BOOL _lookHere;
+}
+
+- (NSString *)description {
+ int *i = NULL;
+ *i = 0;
+ return @"surprise";
+}
+@end
+
+int main() {
+ Bad *bad = [Bad new];
+ printf("break here\n");
+ return 0;
+}
diff --git a/lldb/test/API/lang/objc/struct-description/Makefile b/lldb/test/API/lang/objc/struct-description/Makefile
new file mode 100644
index 0000000..d0aadc1
--- /dev/null
+++ b/lldb/test/API/lang/objc/struct-description/Makefile
@@ -0,0 +1,2 @@
+OBJC_SOURCES := main.m
+include Makefile.rules
diff --git a/lldb/test/API/lang/objc/struct-description/TestObjCStructDescription.py b/lldb/test/API/lang/objc/struct-description/TestObjCStructDescription.py
new file mode 100644
index 0000000..d152b26
--- /dev/null
+++ b/lldb/test/API/lang/objc/struct-description/TestObjCStructDescription.py
@@ -0,0 +1,18 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
+ self.expect(
+ "vo pair",
+ substrs=["error:", "not a pointer type", "(Pair) pair = (f = 2, e = 3)"],
+ )
+ self.expect(
+ "expr -O -- pair",
+ substrs=["error:", "not a pointer type", "(Pair) (f = 2, e = 3)"],
+ )
diff --git a/lldb/test/API/lang/objc/struct-description/main.m b/lldb/test/API/lang/objc/struct-description/main.m
new file mode 100644
index 0000000..5031a28
--- /dev/null
+++ b/lldb/test/API/lang/objc/struct-description/main.m
@@ -0,0 +1,12 @@
+#include <stdio.h>
+
+typedef struct Pair {
+ int f;
+ int e;
+} Pair;
+
+int main() {
+ Pair pair = {2, 3};
+ printf("break here\n");
+ return 0;
+}
diff --git a/lldb/test/API/lit.cfg.py b/lldb/test/API/lit.cfg.py
index 7ab9749..dcff868 100644
--- a/lldb/test/API/lit.cfg.py
+++ b/lldb/test/API/lit.cfg.py
@@ -353,6 +353,7 @@ if platform.system() == "Windows":
# Some steps required to initialize the tests dynamically link with python.dll
# and need to know the location of the Python libraries. This ensures that we
# use the same version of Python that was used to build lldb to run our tests.
+config.environment["PYTHONHOME"] = config.python_root_dir
config.environment["PATH"] = os.path.pathsep.join(
(config.python_root_dir, config.environment.get("PATH", ""))
)
diff --git a/lldb/test/API/python_api/basename/Makefile b/lldb/test/API/python_api/basename/Makefile
new file mode 100644
index 0000000..99998b2
--- /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 0000000..dd82f75
--- /dev/null
+++ b/lldb/test/API/python_api/basename/TestGetBaseName.py
@@ -0,0 +1,37 @@
+"""
+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"])
+ 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 0000000..9b41409
--- /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/find_in_memory/address_ranges_helper.py b/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py
index dcceca6..102f2b0 100644
--- a/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py
+++ b/lldb/test/API/python_api/find_in_memory/address_ranges_helper.py
@@ -55,27 +55,34 @@ def GetRangeFromAddrValue(test_base, addr, shrink=False):
return lldb.SBAddressRange(start, size)
-def IsWithinRange(addr, size, range, target):
- start_addr = range.GetBaseAddress().GetLoadAddress(target)
- end_addr = start_addr + range.GetByteSize()
- addr = addr.GetValueAsUnsigned()
- return addr >= start_addr and addr + size <= end_addr
-
-
def GetHeapRanges(test_base, shrink=False):
frame = test_base.thread.GetSelectedFrame()
ex = frame.EvaluateExpression("heap_pointer1")
test_base.assertTrue(ex.IsValid())
- range = GetRangeFromAddrValue(test_base, ex, shrink)
- addr_ranges = lldb.SBAddressRangeList()
- addr_ranges.Append(range)
+ range1 = GetRangeFromAddrValue(test_base, ex, shrink)
+ range1_start = range1.GetBaseAddress().GetLoadAddress(test_base.target)
+ range1_end = range1_start + range1.GetByteSize()
ex = frame.EvaluateExpression("heap_pointer2")
test_base.assertTrue(ex.IsValid())
- size = len(DOUBLE_INSTANCE_PATTERN_HEAP)
- if not IsWithinRange(ex, size, addr_ranges[0], test_base.target):
- addr_ranges.Append(GetRangeFromAddrValue(test_base, ex, shrink))
+ range2 = GetRangeFromAddrValue(test_base, ex, shrink)
+ range2_start = range2.GetBaseAddress().GetLoadAddress(test_base.target)
+ range2_end = range2_start + range2.GetByteSize()
+
+ addr_ranges = lldb.SBAddressRangeList()
+
+ if range1_end < range2_start or range2_end < range1_start:
+ # The ranges do not overlap; add them both.
+ addr_ranges.Append(range1)
+ addr_ranges.Append(range2)
+ else:
+ # Merge overlapping ranges.
+ base = min(range1_start, range2_start)
+ end = max(range1_end, range2_end)
+ start = lldb.SBAddress(base, test_base.target)
+ size = end - base
+ addr_ranges.Append(lldb.SBAddressRange(start, size))
return addr_ranges
diff --git a/lldb/test/API/python_api/run_locker/TestRunLocker.py b/lldb/test/API/python_api/run_locker/TestRunLocker.py
index b7b4941..d525bbf 100644
--- a/lldb/test/API/python_api/run_locker/TestRunLocker.py
+++ b/lldb/test/API/python_api/run_locker/TestRunLocker.py
@@ -107,4 +107,6 @@ class TestRunLocker(TestBase):
"script var = lldb.frame.EvaluateExpression('SomethingToCall()'); var.GetError().GetCString()",
result,
)
- self.assertIn("sbframe object is not valid", result.GetOutput())
+ self.assertIn(
+ "can't evaluate expressions when the process is running", result.GetOutput()
+ )
diff --git a/lldb/test/API/python_api/sbmodule/Makefile b/lldb/test/API/python_api/sbmodule/Makefile
index 1049594..81df904 100644
--- a/lldb/test/API/python_api/sbmodule/Makefile
+++ b/lldb/test/API/python_api/sbmodule/Makefile
@@ -1,3 +1,12 @@
-C_SOURCES := main.c
+C_SOURCES := main.c a.c b.c
+MAKE_DSYM := NO
+
+all: a.out
+
+a.out: main.o libfoo.a
+ $(LD) $(LDFLAGS) $^ -o $@
+
+libfoo.a: a.o b.o
+ $(AR) $(ARFLAGS) $@ $^
include Makefile.rules
diff --git a/lldb/test/API/python_api/sbmodule/TestSBModule.py b/lldb/test/API/python_api/sbmodule/TestSBModule.py
index c04e2fa..096eadd 100644
--- a/lldb/test/API/python_api/sbmodule/TestSBModule.py
+++ b/lldb/test/API/python_api/sbmodule/TestSBModule.py
@@ -18,6 +18,45 @@ class SBModuleAPICase(TestBase):
if self.background_pid:
os.kill(self.background_pid, signal.SIGKILL)
+ @skipIfRemote
+ def test_GetObjectName(self):
+ """Test the SBModule::GetObjectName() method"""
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ libfoo_path = self.getBuildArtifact("libfoo.a")
+ target_exe = self.dbg.CreateTarget(exe)
+ self.assertTrue(target_exe.IsValid(), "Target for a.out is valid")
+
+ # Test that the executable module has no object name (usually the first module in the target)
+ exe_module = target_exe.GetModuleAtIndex(0)
+ self.assertTrue(exe_module.IsValid(), "Executable module is valid")
+ self.assertIsNone(
+ exe_module.GetObjectName(), "a.out should have no object name"
+ )
+
+ # check archive member names
+ module_specs = lldb.SBModuleSpecList.GetModuleSpecifications(libfoo_path)
+ self.assertGreater(
+ module_specs.GetSize(), 0, "Archive should have at least one module spec"
+ )
+ found = set()
+ expected = {"a.o", "b.o"}
+ for i in range(module_specs.GetSize()):
+ spec = module_specs.GetSpecAtIndex(i)
+ obj_name = spec.GetObjectName()
+ self.assertIsInstance(obj_name, str)
+ self.assertIn(obj_name, expected, f"Unexpected object name: {obj_name}")
+ # create a module from the arhive using the sepc
+ module = lldb.SBModule(spec)
+ self.assertTrue(module.IsValid(), "Module is valid")
+ self.assertTrue(module.IsValid(), f"Module for {obj_name} is valid")
+ self.assertEqual(
+ module.GetObjectName(), obj_name, f"Object name for {obj_name} matches"
+ )
+ found.add(obj_name)
+
+ self.assertEqual(found, expected, "Did not find all expected archive members")
+
@skipUnlessDarwin
@skipIfRemote
def test_module_is_file_backed(self):
diff --git a/lldb/test/API/python_api/sbmodule/a.c b/lldb/test/API/python_api/sbmodule/a.c
new file mode 100644
index 0000000..49f5fff
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/a.c
@@ -0,0 +1,11 @@
+int __a_global = 1;
+
+int a(int arg) {
+ int result = arg + __a_global;
+ return result;
+}
+
+int aa(int arg1) {
+ int result1 = arg1 - __a_global;
+ return result1;
+}
diff --git a/lldb/test/API/python_api/sbmodule/b.c b/lldb/test/API/python_api/sbmodule/b.c
new file mode 100644
index 0000000..4c7011b
--- /dev/null
+++ b/lldb/test/API/python_api/sbmodule/b.c
@@ -0,0 +1,11 @@
+static int __b_global = 2;
+char __extra[4096]; // Make sure sizeof b.o differs from a.o
+int b(int arg) {
+ int result = arg + __b_global;
+ return result;
+}
+
+int bb(int arg1) {
+ int result2 = arg1 - __b_global;
+ return result2;
+}
diff --git a/lldb/test/API/python_api/sbmodule/main.c b/lldb/test/API/python_api/sbmodule/main.c
index 101d495..574d57d 100644
--- a/lldb/test/API/python_api/sbmodule/main.c
+++ b/lldb/test/API/python_api/sbmodule/main.c
@@ -1,3 +1,5 @@
+extern int a(int);
+extern int b(int);
int main() {
while (1) // break here
;
diff --git a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
index 21256d6..99f88d3 100644
--- a/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
+++ b/lldb/test/API/python_api/sbstructureddata/TestStructuredDataAPI.py
@@ -10,7 +10,6 @@ from lldbsuite.test import lldbutil
import json
-
class TestStructuredDataAPI(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@@ -130,6 +129,52 @@ class TestStructuredDataAPI(TestBase):
self.assertSuccess(example.SetFromJSON("null"))
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeNull)
+ example = lldb.SBStructuredData()
+ example.SetUnsignedIntegerValue(1)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeInteger)
+ self.assertEqual(example.GetIntegerValue(), 1)
+
+ example.SetSignedIntegerValue(-42)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeSignedInteger)
+ self.assertEqual(example.GetSignedIntegerValue(), -42)
+
+ example.SetFloatValue(4.19)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeFloat)
+ self.assertEqual(example.GetFloatValue(), 4.19)
+
+ example.SetStringValue("Bonjour, 123!")
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
+ self.assertEqual(example.GetStringValue(42), "Bonjour, 123!")
+
+ value = lldb.SBStructuredData()
+ example.SetValueForKey("Hello", value)
+ self.assertEqual(example.GetSize(), 0)
+
+ nested_obj = lldb.SBStructuredData()
+ nested_obj.SetStringValue("World")
+ example.SetValueForKey("Hello", nested_obj)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeDictionary)
+ nested_obj = None
+ nested_obj = example.GetValueForKey("Hello")
+ self.assertTrue(nested_obj.IsValid())
+ self.assertEqual(nested_obj.GetType(), lldb.eStructuredDataTypeString)
+ self.assertEqual(nested_obj.GetStringValue(42), "World")
+
+ example.SetBooleanValue(True)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeBoolean)
+ self.assertTrue(example.GetBooleanValue())
+
+ rnd_obj = MyRandomClass()
+ stp = lldb.SBScriptObject(rnd_obj, lldb.eScriptLanguagePython)
+ self.assertEqual(stp.ptr, rnd_obj)
+
+ example.SetGenericValue(stp)
+ self.assertEqual(example.GetType(), lldb.eStructuredDataTypeGeneric)
+
+ my_random_class = example.GetGenericValue()
+ self.assertTrue(my_random_class)
+ self.assertEqual(my_random_class.payload, MyRandomClass.payload)
+
example_arr = [1, 2.3, "4", {"5": False}]
arr_str = json.dumps(example_arr)
s.Clear()
diff --git a/lldb/test/API/python_api/value/TestValueAPI.py b/lldb/test/API/python_api/value/TestValueAPI.py
index 0da5734..907992b 100644
--- a/lldb/test/API/python_api/value/TestValueAPI.py
+++ b/lldb/test/API/python_api/value/TestValueAPI.py
@@ -83,7 +83,7 @@ class ValueAPITestCase(TestBase):
fmt = lldbutil.BasicFormatter()
cvf = lldbutil.ChildVisitingFormatter(indent_child=2)
- rdf = lldbutil.RecursiveDecentFormatter(indent_child=2)
+ rdf = lldbutil.RecursiveDescentFormatter(indent_child=2)
if self.TraceOn():
print(fmt.format(days_of_week))
print(cvf.format(days_of_week))
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 55557e6..d7d25ca 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",
@@ -153,7 +155,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
breakpoint_ids = self.set_function_breakpoints(functions)
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
self.continue_to_breakpoints(breakpoint_ids)
- output = self.collect_console(timeout_secs=10, pattern=stopCommands[-1])
+ output = self.collect_console(timeout=10, pattern=stopCommands[-1])
self.verify_commands("stopCommands", output, stopCommands)
# Continue after launch and hit the "pause()" call and stop the target.
@@ -163,7 +165,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
time.sleep(0.5)
self.dap_server.request_pause()
self.dap_server.wait_for_stopped()
- output = self.collect_console(timeout_secs=10, pattern=stopCommands[-1])
+ output = self.collect_console(timeout=10, pattern=stopCommands[-1])
self.verify_commands("stopCommands", output, stopCommands)
# Continue until the program exits
@@ -172,7 +174,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
# "exitCommands" that were run after the second breakpoint was hit
# and the "terminateCommands" due to the debugging session ending
output = self.collect_console(
- timeout_secs=10.0,
+ timeout=10.0,
pattern=terminateCommands[0],
)
self.verify_commands("exitCommands", output, exitCommands)
@@ -223,7 +225,7 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
# "terminateCommands"
self.dap_server.request_disconnect(terminateDebuggee=True)
output = self.collect_console(
- timeout_secs=1.0,
+ timeout=1.0,
pattern=terminateCommands[0],
)
self.verify_commands("terminateCommands", output, terminateCommands)
diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py b/lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py
index 674bfe4..fab109c 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py
@@ -2,7 +2,6 @@
Test lldb-dap setBreakpoints request in assembly source references.
"""
-
from lldbsuite.test.decorators import *
from dap_server import Source
import lldbdap_testcase
@@ -52,7 +51,7 @@ class TestDAP_setBreakpointsAssembly(lldbdap_testcase.DAPTestCaseBase):
# Verify that setting a breakpoint on an invalid source reference fails
response = self.dap_server.request_setBreakpoints(
- Source(source_reference=-1), [1]
+ Source.build(source_reference=-1), [1]
)
self.assertIsNotNone(response)
breakpoints = response["body"]["breakpoints"]
@@ -69,7 +68,7 @@ class TestDAP_setBreakpointsAssembly(lldbdap_testcase.DAPTestCaseBase):
# Verify that setting a breakpoint on a source reference that is not created fails
response = self.dap_server.request_setBreakpoints(
- Source(source_reference=200), [1]
+ Source.build(source_reference=200), [1]
)
self.assertIsNotNone(response)
breakpoints = response["body"]["breakpoints"]
@@ -83,3 +82,79 @@ class TestDAP_setBreakpointsAssembly(lldbdap_testcase.DAPTestCaseBase):
break_point["message"],
"Invalid sourceReference.",
)
+
+ @skipIfWindows
+ def test_persistent_assembly_breakpoint(self):
+ """Tests that assembly breakpoints are working persistently across sessions"""
+ self.build()
+ program = self.getBuildArtifact("a.out")
+ self.create_debug_adapter()
+
+ # Run the first session and set a persistent assembly breakpoint
+ try:
+ self.dap_server.request_initialize()
+ self.dap_server.request_launch(program)
+
+ assmebly_func_breakpoints = self.set_function_breakpoints(["assembly_func"])
+ self.continue_to_breakpoints(assmebly_func_breakpoints)
+
+ assembly_func_frame = self.get_stackFrames()[0]
+ source_reference = assembly_func_frame["source"]["sourceReference"]
+
+ # Set an assembly breakpoint in the middle of the assembly function
+ persistent_breakpoint_line = 4
+ persistent_breakpoint_ids = self.set_source_breakpoints_assembly(
+ source_reference, [persistent_breakpoint_line]
+ )
+
+ self.assertEqual(
+ len(persistent_breakpoint_ids),
+ 1,
+ "Expected one assembly breakpoint to be set",
+ )
+
+ persistent_breakpoint_source = self.dap_server.resolved_breakpoints[
+ persistent_breakpoint_ids[0]
+ ]["source"]
+ self.assertIn(
+ "adapterData",
+ persistent_breakpoint_source,
+ "Expected assembly breakpoint to have persistent information",
+ )
+ self.assertIn(
+ "persistenceData",
+ persistent_breakpoint_source["adapterData"],
+ "Expected assembly breakpoint to have persistent information",
+ )
+
+ self.continue_to_breakpoints(persistent_breakpoint_ids)
+ finally:
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ self.dap_server.terminate()
+
+ # Restart the session and verify the breakpoint is still there
+ self.create_debug_adapter()
+ try:
+ self.dap_server.request_initialize()
+ self.dap_server.request_launch(program)
+ new_session_breakpoints_ids = self.set_source_breakpoints_from_source(
+ Source(persistent_breakpoint_source),
+ [persistent_breakpoint_line],
+ )
+
+ self.assertEqual(
+ len(new_session_breakpoints_ids),
+ 1,
+ "Expected one breakpoint to be set in the new session",
+ )
+
+ self.continue_to_breakpoints(new_session_breakpoints_ids)
+ current_line = self.get_stackFrames()[0]["line"]
+ self.assertEqual(
+ current_line,
+ persistent_breakpoint_line,
+ "Expected to hit the persistent assembly breakpoint at the same line",
+ )
+ finally:
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ self.dap_server.terminate()
diff --git a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
index 8f03244..151ad76 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py
@@ -58,7 +58,7 @@ class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase):
# Set breakpoints and verify that they got set correctly
dap_breakpoint_ids = []
response = self.dap_server.request_setBreakpoints(
- Source(main_source_path), [main_bp_line]
+ Source.build(path=main_source_path), [main_bp_line]
)
self.assertTrue(response["success"])
breakpoints = response["body"]["breakpoints"]
@@ -70,7 +70,7 @@ class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase):
)
response = self.dap_server.request_setBreakpoints(
- Source(foo_source_path), [foo_bp1_line]
+ Source.build(path=foo_source_path), [foo_bp1_line]
)
self.assertTrue(response["success"])
breakpoints = response["body"]["breakpoints"]
diff --git a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
index 2e860ff..3309800 100644
--- a/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
+++ b/lldb/test/API/tools/lldb-dap/breakpoint/TestDAP_setBreakpoints.py
@@ -2,7 +2,6 @@
Test lldb-dap setBreakpoints request
"""
-
from dap_server import Source
import shutil
from lldbsuite.test.decorators import *
@@ -58,7 +57,7 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# breakpoint in main.cpp
response = self.dap_server.request_setBreakpoints(
- Source(new_main_path), [main_line]
+ Source.build(path=new_main_path), [main_line]
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(len(breakpoints), 1)
@@ -70,7 +69,7 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# 2nd breakpoint, which is from a dynamically loaded library
response = self.dap_server.request_setBreakpoints(
- Source(new_other_path), [other_line]
+ Source.build(path=new_other_path), [other_line]
)
breakpoints = response["body"]["breakpoints"]
breakpoint = breakpoints[0]
@@ -85,7 +84,7 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# 2nd breakpoint again, which should be valid at this point
response = self.dap_server.request_setBreakpoints(
- Source(new_other_path), [other_line]
+ Source.build(path=new_other_path), [other_line]
)
breakpoints = response["body"]["breakpoints"]
breakpoint = breakpoints[0]
@@ -129,7 +128,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
self.build_and_launch(program)
# Set 3 breakpoints and verify that they got set correctly
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
line_to_id = {}
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
@@ -154,7 +155,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
lines.remove(second_line)
# Set 2 breakpoints and verify that the previous breakpoints that were
# set above are still set.
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
len(breakpoints),
@@ -199,7 +202,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# Now clear all breakpoints for the source file by passing down an
# empty lines array
lines = []
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
len(breakpoints),
@@ -219,7 +224,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# Now set a breakpoint again in the same source file and verify it
# was added.
lines = [second_line]
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
if response:
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
@@ -270,7 +277,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
self.build_and_launch(program)
# Set one breakpoint and verify that it got set correctly.
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
line_to_id = {}
breakpoints = response["body"]["breakpoints"]
self.assertEqual(
@@ -286,7 +295,9 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# Now clear all breakpoints for the source file by not setting the
# lines array.
lines = None
- response = self.dap_server.request_setBreakpoints(Source(self.main_path), lines)
+ response = self.dap_server.request_setBreakpoints(
+ Source.build(path=self.main_path), lines
+ )
breakpoints = response["body"]["breakpoints"]
self.assertEqual(len(breakpoints), 0, "expect no source breakpoints")
@@ -362,7 +373,7 @@ class TestDAP_setBreakpoints(lldbdap_testcase.DAPTestCaseBase):
# Set two breakpoints on the loop line at different columns.
columns = [13, 39]
response = self.dap_server.request_setBreakpoints(
- Source(self.main_path),
+ Source.build(path=self.main_path),
[loop_line, loop_line],
list({"column": c} for c in columns),
)
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 824ed8f..109f34f 100644
--- a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
+++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
@@ -10,16 +10,14 @@ import lldbdap_testcase
class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
- def send_async_req(self, command: str, arguments={}) -> int:
- seq = self.dap_server.sequence
- self.dap_server.send_packet(
+ def send_async_req(self, command: str, arguments: dict = {}) -> int:
+ return self.dap_server.send_packet(
{
"type": "request",
"command": command,
"arguments": arguments,
}
)
- return seq
def async_blocking_request(self, duration: float) -> int:
"""
@@ -39,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.
@@ -54,18 +53,18 @@ class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
pending_seq = self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2)
cancel_seq = self.async_cancel(requestId=pending_seq)
- blocking_resp = self.dap_server.recv_packet(filter_type=["response"])
+ blocking_resp = self.dap_server.receive_response(blocking_seq)
self.assertEqual(blocking_resp["request_seq"], blocking_seq)
self.assertEqual(blocking_resp["command"], "evaluate")
self.assertEqual(blocking_resp["success"], True)
- pending_resp = self.dap_server.recv_packet(filter_type=["response"])
+ pending_resp = self.dap_server.receive_response(pending_seq)
self.assertEqual(pending_resp["request_seq"], pending_seq)
self.assertEqual(pending_resp["command"], "evaluate")
self.assertEqual(pending_resp["success"], False)
self.assertEqual(pending_resp["message"], "cancelled")
- cancel_resp = self.dap_server.recv_packet(filter_type=["response"])
+ cancel_resp = self.dap_server.receive_response(cancel_seq)
self.assertEqual(cancel_resp["request_seq"], cancel_seq)
self.assertEqual(cancel_resp["command"], "cancel")
self.assertEqual(cancel_resp["success"], True)
@@ -80,19 +79,16 @@ class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
blocking_seq = self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2)
# Wait for the sleep to start to cancel the inflight request.
- self.collect_console(
- timeout_secs=self.DEFAULT_TIMEOUT,
- pattern="starting sleep",
- )
+ self.collect_console(pattern="starting sleep")
cancel_seq = self.async_cancel(requestId=blocking_seq)
- blocking_resp = self.dap_server.recv_packet(filter_type=["response"])
+ blocking_resp = self.dap_server.receive_response(blocking_seq)
self.assertEqual(blocking_resp["request_seq"], blocking_seq)
self.assertEqual(blocking_resp["command"], "evaluate")
self.assertEqual(blocking_resp["success"], False)
self.assertEqual(blocking_resp["message"], "cancelled")
- cancel_resp = self.dap_server.recv_packet(filter_type=["response"])
+ cancel_resp = self.dap_server.receive_response(cancel_seq)
self.assertEqual(cancel_resp["request_seq"], cancel_seq)
self.assertEqual(cancel_resp["command"], "cancel")
self.assertEqual(cancel_resp["success"], True)
diff --git a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
index ea6b2ea..e61d248 100644
--- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
+++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
@@ -1,8 +1,8 @@
-import os
+"""
+Test lldb-dap command hooks
+"""
-import dap_server
import lldbdap_testcase
-from lldbsuite.test import lldbtest, lldbutil
from lldbsuite.test.decorators import *
@@ -23,7 +23,7 @@ class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase):
exitCommands=["?" + command_quiet, command_not_quiet],
)
full_output = self.collect_console(
- timeout_secs=1.0,
+ timeout=1.0,
pattern=command_not_quiet,
)
self.assertNotIn(command_quiet, full_output)
@@ -51,7 +51,7 @@ class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase):
expectFailure=True,
)
full_output = self.collect_console(
- timeout_secs=1.0,
+ timeout=1.0,
pattern=command_abort_on_error,
)
self.assertNotIn(command_quiet, full_output)
@@ -81,9 +81,6 @@ class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase):
expectFailure=True,
)
self.assertFalse(resp["success"], "expected 'attach' failure")
- full_output = self.collect_console(
- timeout_secs=1.0,
- pattern=command_abort_on_error,
- )
+ full_output = self.collect_console(pattern=command_abort_on_error)
self.assertNotIn(command_quiet, full_output)
self.assertIn(command_abort_on_error, full_output)
diff --git a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
index 04897ac..954a3a4 100644
--- a/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
+++ b/lldb/test/API/tools/lldb-dap/completions/TestDAP_completions.py
@@ -189,7 +189,7 @@ class TestDAP_completions(lldbdap_testcase.DAPTestCaseBase):
self.dap_server.get_completions("str"),
[
{"text": "struct", "label": "struct"},
- {"text": "str1", "label": "str1 -- string &"},
+ {"text": "str1", "label": "str1 -- std::string &"},
],
)
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 811843d..202b23a 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
@@ -139,9 +140,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
process.wait()
# Get the console output
- console_output = self.collect_console(
- timeout_secs=10.0, pattern="exited with status"
- )
+ console_output = self.collect_console(pattern="exited with status")
# Verify the exit status message is printed.
self.assertRegex(
@@ -156,9 +155,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_exit()
# Get the console output
- console_output = self.collect_console(
- timeout_secs=10.0, pattern="exited with status"
- )
+ console_output = self.collect_console(pattern="exited with status")
# Verify the exit status message is printed.
self.assertIn(
@@ -177,9 +174,7 @@ class TestDAP_console(lldbdap_testcase.DAPTestCaseBase):
f"target create --core {core}", context="repl"
)
- diagnostics = self.collect_important(
- timeout_secs=self.DEFAULT_TIMEOUT, pattern="minidump file"
- )
+ diagnostics = self.collect_important(pattern="minidump file")
self.assertIn(
"warning: unable to retrieve process ID from minidump file",
diff --git a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
index b8b266b..8bb9ea2 100644
--- a/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
+++ b/lldb/test/API/tools/lldb-dap/instruction-breakpoint/TestDAP_instruction_breakpoint.py
@@ -34,7 +34,7 @@ class TestDAP_InstructionBreakpointTestCase(lldbdap_testcase.DAPTestCaseBase):
# Set source breakpoint 1
response = self.dap_server.request_setBreakpoints(
- Source(self.main_path), [main_line]
+ Source.build(path=self.main_path), [main_line]
)
breakpoints = response["body"]["breakpoints"]
self.assertEqual(len(breakpoints), 1)
diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
index b72b98d..af5c62a 100644
--- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
+++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
@@ -8,6 +8,9 @@ from lldbsuite.test.decorators import *
import lldbdap_testcase
import dap_server
+EXIT_FAILURE = 1
+EXIT_SUCCESS = 0
+
class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
def launch(self):
@@ -41,40 +44,44 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
"""
process = self.launch()
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), 0)
+ self.assertEqual(process.wait(timeout=5.0), EXIT_SUCCESS)
def test_invalid_header(self):
"""
- lldb-dap handles invalid message headers.
+ lldb-dap returns a failure exit code when the input stream is closed
+ with a malformed request header.
"""
process = self.launch()
- process.stdin.write(b"not the corret message header")
+ process.stdin.write(b"not the correct message header")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), 1)
+ self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
def test_partial_header(self):
"""
- lldb-dap handles parital message headers.
+ lldb-dap returns a failure exit code when the input stream is closed
+ with an incomplete message header is in the message buffer.
"""
process = self.launch()
process.stdin.write(b"Content-Length: ")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), 1)
+ self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
def test_incorrect_content_length(self):
"""
- lldb-dap handles malformed content length headers.
+ lldb-dap returns a failure exit code when reading malformed content
+ length headers.
"""
process = self.launch()
process.stdin.write(b"Content-Length: abc")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), 1)
+ self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
def test_partial_content_length(self):
"""
- lldb-dap handles partial messages.
+ lldb-dap returns a failure exit code when the input stream is closed
+ with a partial message in the message buffer.
"""
process = self.launch()
process.stdin.write(b"Content-Length: 10\r\n\r\n{")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), 1)
+ self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
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 a611cc3..41112a4 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -2,12 +2,9 @@
Test lldb-dap setBreakpoints request
"""
-import dap_server
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
import lldbdap_testcase
-import time
import os
import re
@@ -208,7 +205,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_exit()
# Now get the STDOUT and verify our program argument is correct
output = self.get_stdout()
- self.assertEqual(output, None, "expect no program output")
+ self.assertEqual(output, "", "expect no program output")
@skipIfWindows
@skipIfLinux # shell argument expansion doesn't seem to work on Linux
@@ -409,14 +406,14 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
# Get output from the console. This should contain both the
# "stopCommands" that were run after the first breakpoint was hit
self.continue_to_breakpoints(breakpoint_ids)
- output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ output = self.get_console()
self.verify_commands("stopCommands", output, stopCommands)
# Continue again and hit the second breakpoint.
# Get output from the console. This should contain both the
# "stopCommands" that were run after the second breakpoint was hit
self.continue_to_breakpoints(breakpoint_ids)
- output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ output = self.get_console()
self.verify_commands("stopCommands", output, stopCommands)
# Continue until the program exits
@@ -424,13 +421,12 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
# Get output from the console. This should contain both the
# "exitCommands" that were run after the second breakpoint was hit
# and the "terminateCommands" due to the debugging session ending
- output = self.collect_console(
- timeout_secs=1.0,
- pattern=terminateCommands[0],
- )
+ output = self.collect_console(pattern=terminateCommands[0])
self.verify_commands("exitCommands", output, exitCommands)
self.verify_commands("terminateCommands", output, terminateCommands)
+ # Flakey on 32-bit Arm Linux.
+ @skipIf(oslist=["linux"], archs=["arm$"])
def test_extra_launch_commands(self):
"""
Tests the "launchCommands" with extra launching settings
@@ -478,21 +474,21 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
self.verify_commands("launchCommands", output, launchCommands)
# Verify the "stopCommands" here
self.continue_to_next_stop()
- output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ output = self.get_console()
self.verify_commands("stopCommands", output, stopCommands)
# Continue and hit the second breakpoint.
# Get output from the console. This should contain both the
# "stopCommands" that were run after the first breakpoint was hit
self.continue_to_next_stop()
- output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ output = self.get_console()
self.verify_commands("stopCommands", output, stopCommands)
# Continue until the program exits
self.continue_to_exit()
# Get output from the console. This should contain both the
# "exitCommands" that were run after the second breakpoint was hit
- output = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ output = self.get_console()
self.verify_commands("exitCommands", output, exitCommands)
def test_failing_launch_commands(self):
@@ -556,10 +552,7 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
# Once it's disconnected the console should contain the
# "terminateCommands"
self.dap_server.request_disconnect(terminateDebuggee=True)
- output = self.collect_console(
- timeout_secs=1.0,
- pattern=terminateCommands[0],
- )
+ output = self.collect_console(pattern=terminateCommands[0])
self.verify_commands("terminateCommands", output, terminateCommands)
@skipIfWindows
diff --git a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
index 2c3c1fe..f51056d 100644
--- a/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
+++ b/lldb/test/API/tools/lldb-dap/memory/TestDAP_memory.py
@@ -126,6 +126,8 @@ class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_exit()
+ # Flakey on 32-bit Arm Linux.
+ @skipIf(oslist=["linux"], archs=["arm$"])
def test_writeMemory(self):
"""
Tests the 'writeMemory' request
diff --git a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
index 64ed415..bb835af 100644
--- a/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
+++ b/lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py
@@ -23,15 +23,15 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_breakpoints(breakpoint_ids)
# We're now stopped at breakpoint 1 before the dlopen. Flush all the module events.
- event = self.dap_server.wait_for_event("module", 0.25)
+ event = self.dap_server.wait_for_event(["module"], 0.25)
while event is not None:
- event = self.dap_server.wait_for_event("module", 0.25)
+ event = self.dap_server.wait_for_event(["module"], 0.25)
# Continue to the second breakpoint, before the dlclose.
self.continue_to_breakpoints(breakpoint_ids)
# Make sure we got a module event for libother.
- event = self.dap_server.wait_for_event("module", 5)
+ event = self.dap_server.wait_for_event(["module"], 5)
self.assertIsNotNone(event, "didn't get a module event")
module_name = event["body"]["module"]["name"]
module_id = event["body"]["module"]["id"]
@@ -42,7 +42,7 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_breakpoints(breakpoint_ids)
# Make sure we got a module event for libother.
- event = self.dap_server.wait_for_event("module", 5)
+ event = self.dap_server.wait_for_event(["module"], 5)
self.assertIsNotNone(event, "didn't get a module event")
reason = event["body"]["reason"]
self.assertEqual(reason, "removed")
@@ -56,7 +56,7 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase):
self.assertEqual(module_data["name"], "", "expects empty name.")
# Make sure we do not send another event
- event = self.dap_server.wait_for_event("module", 3)
+ event = self.dap_server.wait_for_event(["module"], 3)
self.assertIsNone(event, "expects no events.")
self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
index c9091df..74743d9 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -1,11 +1,9 @@
"""
-Test lldb-dap setBreakpoints request
+Test lldb-dap module request
"""
-import dap_server
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
import lldbdap_testcase
import re
@@ -55,7 +53,7 @@ class TestDAP_module(lldbdap_testcase.DAPTestCaseBase):
if expect_debug_info_size:
self.assertTrue(
- self.waitUntil(check_symbols_loaded_with_size),
+ self.wait_until(check_symbols_loaded_with_size),
"expect has debug info size",
)
@@ -68,7 +66,7 @@ class TestDAP_module(lldbdap_testcase.DAPTestCaseBase):
# Collect all the module names we saw as events.
module_new_names = []
module_changed_names = []
- module_event = self.dap_server.wait_for_event("module", 1)
+ module_event = self.dap_server.wait_for_event(["module"], 1)
while module_event is not None:
reason = module_event["body"]["reason"]
if reason == "new":
@@ -76,7 +74,7 @@ class TestDAP_module(lldbdap_testcase.DAPTestCaseBase):
elif reason == "changed":
module_changed_names.append(module_event["body"]["module"]["name"])
- module_event = self.dap_server.wait_for_event("module", 1)
+ module_event = self.dap_server.wait_for_event(["module"], 1)
# Make sure we got an event for every active module.
self.assertNotEqual(len(module_new_names), 0)
diff --git a/lldb/test/API/tools/lldb-dap/moduleSymbols/Makefile b/lldb/test/API/tools/lldb-dap/moduleSymbols/Makefile
new file mode 100644
index 0000000..1049594
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/moduleSymbols/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/moduleSymbols/TestDAP_moduleSymbols.py b/lldb/test/API/tools/lldb-dap/moduleSymbols/TestDAP_moduleSymbols.py
new file mode 100644
index 0000000..2336b9f
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/moduleSymbols/TestDAP_moduleSymbols.py
@@ -0,0 +1,40 @@
+"""
+Test lldb-dap moduleSymbols request
+"""
+
+import lldbdap_testcase
+from lldbsuite.test.decorators import *
+
+
+class TestDAP_moduleSymbols(lldbdap_testcase.DAPTestCaseBase):
+ # On windows LLDB doesn't recognize symbols in a.out.
+ @skipIfWindows
+ def test_moduleSymbols(self):
+ """
+ Test that the moduleSymbols request returns correct symbols from the module.
+ """
+ program = self.getBuildArtifact("a.out")
+ self.build_and_launch(program)
+
+ symbol_names = []
+ i = 0
+ while True:
+ next_symbol = self.dap_server.request_moduleSymbols(
+ moduleName="a.out", startIndex=i, count=1
+ )
+ self.assertIn("symbols", next_symbol["body"])
+ result_symbols = next_symbol["body"]["symbols"]
+ self.assertLessEqual(len(result_symbols), 1)
+ if len(result_symbols) == 0:
+ break
+
+ self.assertIn("name", result_symbols[0])
+ symbol_names.append(result_symbols[0]["name"])
+ i += 1
+ if i >= 1000:
+ break
+
+ self.assertGreater(len(symbol_names), 0)
+ self.assertIn("main", symbol_names)
+ self.assertIn("func1", symbol_names)
+ self.assertIn("func2", symbol_names)
diff --git a/lldb/test/API/tools/lldb-dap/moduleSymbols/main.c b/lldb/test/API/tools/lldb-dap/moduleSymbols/main.c
new file mode 100644
index 0000000..b038b10
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/moduleSymbols/main.c
@@ -0,0 +1,9 @@
+int func1() { return 42; }
+
+int func2() { return 84; }
+
+int main() {
+ func1();
+ func2();
+ return 0;
+}
diff --git a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
index 0425b55..fe978a9 100644
--- a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
+++ b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
@@ -29,7 +29,7 @@ class TestDAP_output(lldbdap_testcase.DAPTestCaseBase):
self.continue_to_breakpoints(breakpoint_ids)
# Ensure partial messages are still sent.
- output = self.collect_stdout(timeout_secs=1.0, pattern="abcdef")
+ output = self.collect_stdout(timeout=1.0, pattern="abcdef")
self.assertTrue(output and len(output) > 0, "expect program stdout")
self.continue_to_exit()
@@ -37,14 +37,14 @@ class TestDAP_output(lldbdap_testcase.DAPTestCaseBase):
# Disconnecting from the server to ensure any pending IO is flushed.
self.dap_server.request_disconnect()
- output += self.get_stdout(timeout=self.DEFAULT_TIMEOUT)
+ output += self.get_stdout()
self.assertTrue(output and len(output) > 0, "expect program stdout")
self.assertIn(
"abcdefghi\r\nhello world\r\nfinally\0\0",
output,
"full stdout not found in: " + repr(output),
)
- console = self.get_console(timeout=self.DEFAULT_TIMEOUT)
+ console = self.get_console()
self.assertTrue(console and len(console) > 0, "expect dap messages")
self.assertIn(
"out\0\0\r\nerr\0\0\r\n", console, f"full console message not found"
diff --git a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
index b47d529..3f57dfb 100755
--- a/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
+++ b/lldb/test/API/tools/lldb-dap/progress/TestDAP_Progress.py
@@ -21,7 +21,7 @@ class TestDAP_progress(lldbdap_testcase.DAPTestCaseBase):
expected_not_in_message=None,
only_verify_first_update=False,
):
- self.dap_server.wait_for_event("progressEnd")
+ self.dap_server.wait_for_event(["progressEnd"])
self.assertTrue(len(self.dap_server.progress_events) > 0)
start_found = False
update_found = False
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 b487257..1d13bcd 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/Shell/Expr/TestLambdaExprImport.test b/lldb/test/Shell/Expr/TestLambdaExprImport.test
new file mode 100644
index 0000000..c57ce06
--- /dev/null
+++ b/lldb/test/Shell/Expr/TestLambdaExprImport.test
@@ -0,0 +1,26 @@
+# Test that we can successfully ASTImport clang::LambdaExpr nodes.
+# Currently this is not supported in MinimalImport mode (which LLDB
+# uses always).
+
+# RUN: split-file %s %t
+# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out
+# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN: -x -b -s %t/commands.input %t.out 2>&1 \
+# RUN: | FileCheck %s
+
+#--- main.cpp
+
+int main() {
+ __builtin_debugtrap();
+}
+
+#--- commands.input
+
+run
+expression --top-level -- void method(int x) { [x=x] { ; }; }
+target dump typesystem
+
+# CHECK: expression
+# CHECK: target dump typesystem
+# CHECK-NOT: FunctionDecl
+# CHECK-NOT: LambdaExpr
diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test
index a5e95cf..872b5a7a 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/SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp b/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp
index 415b485..9251930 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-foreign-type-units.cpp
@@ -56,8 +56,8 @@
// DWPMAIN-NEXT: struct CustomType {
// DWPMAIN-NEXT: typedef int IntegerType;
// DWPMAIN-NEXT: typedef double FloatType;
-// DWPMAIN-NEXT: CustomType::IntegerType x;
-// DWPMAIN-NEXT: CustomType::FloatType y;
+// DWPMAIN-NEXT: IntegerType x;
+// DWPMAIN-NEXT: FloatType y;
// DWPMAIN-NEXT: }
// Next we check when we make the .dwp file with %t.foo.dwo first so it will
@@ -78,8 +78,8 @@
// DWPFOO-NEXT: struct CustomType {
// DWPFOO-NEXT: typedef unsigned int IntegerType;
// DWPFOO-NEXT: typedef float FloatType;
-// DWPFOO-NEXT: CustomType::IntegerType x;
-// DWPFOO-NEXT: CustomType::FloatType y;
+// DWPFOO-NEXT: IntegerType x;
+// DWPFOO-NEXT: FloatType y;
// DWPFOO-NEXT: }
struct CustomType {
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
index 5ebef61..3ef7a4c 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
@@ -6,24 +6,52 @@
// RUN: lldb-test symbols --find=function --name=main --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-MAIN
+// RUN: lldb-test symbols --find=function --name=main --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=main --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-MAIN
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC
+// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-STATIC
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VAR
+// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-VAR
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
+// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
+// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
+// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
+// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=full %t.exe \
// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD
+// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=full %t.exe \
-// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD
+// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-FULL
+// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=method %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-METHOD
+// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=base %t.exe \
+// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-BASE
struct Struct {
int simple_method() {
@@ -51,7 +79,28 @@ private:
}
};
+class Class {
+public:
+ bool overloaded_method() {
+ return false;
+ }
+ bool overloaded_method(int i) {
+ return i > 0;
+ }
+ static int overloaded_method(bool b) {
+ return b ? 1 : 2;
+ }
+};
+
+char overloaded_method() {
+ return 0;
+}
+char overloaded_method(int i) {
+ return 0;
+}
+
Struct s;
+Class c;
static int static_fn() {
return 42;
@@ -63,12 +112,16 @@ int varargs_fn(int x, int y, ...) {
int main(int argc, char **argv) {
return static_fn() + varargs_fn(argc, argc) + s.simple_method() +
- Struct::static_method() + s.virtual_method() + s.overloaded_method();
+ Struct::static_method() + s.virtual_method() + s.overloaded_method() +
+ Class::overloaded_method(false) + c.overloaded_method(1) + c.overloaded_method()
+ + overloaded_method() + overloaded_method(1);
}
// FIND-MAIN: Function: id = {{.*}}, name = "main"
// FIND-MAIN-NEXT: FuncType: id = {{.*}}, compiler_type = "int (int, char **)"
+// FIND-NO-FUNCTION: Found 0 functions
+
// FIND-STATIC: Function: id = {{.*}}, name = "{{.*}}static_fn{{.*}}"
// FIND-STATIC-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
@@ -84,7 +137,29 @@ int main(int argc, char **argv) {
// FIND-STATIC-METHOD: Function: id = {{.*}}, name = "{{.*}}Struct::static_method{{.*}}"
// FIND-STATIC-METHOD-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
-// FIND-OVERLOAD: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (void)"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char)"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
+// FIND-OVERLOAD-FULL-NOT: "Class::overloaded_method"
+// FIND-OVERLOAD-FULL-NOT: "overloaded_method"
+// FIND-OVERLOAD-FULL-DAG: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-FULL-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
+// FIND-OVERLOAD-FULL-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
+// FIND-OVERLOAD-FULL-DAG: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
+
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "Class::overloaded_method"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (int)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (_Bool)"
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "overloaded_method"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (int)"
+
+// FIND-OVERLOAD-METHOD-NOT: "overloaded_method"
+// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
+// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = "Class::overloaded_method"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (void)"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (int)"
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/unknown-udt-decl.ll b/lldb/test/Shell/SymbolFile/NativePDB/unknown-udt-decl.ll
new file mode 100644
index 0000000..af78789
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/unknown-udt-decl.ll
@@ -0,0 +1,56 @@
+; Test that the declaration for UDTs won't be "<unknown>" or "\<unknown>".
+; Rustc sets the location of some builtin types to this string.
+
+; REQUIRES: system-windows
+; RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s
+; RUN: lldb-test symbols %t.exe | FileCheck %s
+
+; there shouldn't be a declaration (would be between size and compiler_type)
+; CHECK: Type{{.*}} , name = "Foo", size = 1, compiler_type = {{.*}} struct Foo {
+
+; This is edited output from clang simulates rustc behavior (see !17)
+; Source:
+; struct Foo {};
+;
+; int main() { Foo f; }
+
+
+; ModuleID = 'main.cpp'
+source_filename = "main.cpp"
+target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.44.35207"
+
+%struct.Foo = type { i8 }
+
+; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable
+define dso_local noundef i32 @main() #0 !dbg !9 {
+ %1 = alloca %struct.Foo, align 1
+ #dbg_declare(ptr %1, !14, !DIExpression(), !16)
+ ret i32 0, !dbg !16
+}
+
+attributes #0 = { mustprogress noinline norecurse nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 20.1.6", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "main.cpp", directory: "F:\\Dev\\rust-dbg-test", checksumkind: CSK_MD5, checksum: "b8942260dadf9ec35328889f05afb954")
+!2 = !{i32 2, !"CodeView", i32 1}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 2}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"uwtable", i32 2}
+!7 = !{i32 1, !"MaxTLSAlign", i32 65536}
+!8 = !{!"clang version 20.1.6"}
+!9 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !13)
+!10 = !DISubroutineType(types: !11)
+!11 = !{!12}
+!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!13 = !{}
+!14 = !DILocalVariable(name: "f", scope: !9, file: !1, line: 3, type: !15)
+!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !17, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !13, identifier: ".?AUFoo@@")
+!16 = !DILocation(line: 3, scope: !9)
+; This is how rustc emits some types
+!17 = !DIFile(filename: "<unknown>", directory: "")
diff --git a/lldb/test/Shell/SymbolFile/PDB/ast-restore.test b/lldb/test/Shell/SymbolFile/PDB/ast-restore.test
index a91364b..f127acd 100644
--- a/lldb/test/Shell/SymbolFile/PDB/ast-restore.test
+++ b/lldb/test/Shell/SymbolFile/PDB/ast-restore.test
@@ -3,13 +3,19 @@ RUN: %build --compiler=msvc --nodefaultlib --output=%t.exe %S/Inputs/AstRestoreT
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=ENUM %s
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=ENUM %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=GLOBAL %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=GLOBAL %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=BASE %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=BASE %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=CLASS %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=INNER %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=INNER %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=TEMPLATE %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=TEMPLATE %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=FOO %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=FOO %s
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=MAIN %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix=MAIN %s
ENUM: Module: {{.*}}
ENUM: namespace N0 {
diff --git a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test
index 7dabf91..8b0b5b6 100644
--- a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test
+++ b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-arm.test
@@ -1,7 +1,12 @@
REQUIRES: target-windows, lld, (target-arm || target-aarch64)
+
RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp
+RUN: lldb-test symbols -dump-ast %t.exe | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck %s
+
RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp
RUN: lldb-test symbols -dump-ast %t.exe | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t.exe | FileCheck %s
CHECK: Module: {{.*}}
CHECK-DAG: int (*FuncCCallPtr)();
diff --git a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-x86.test b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-x86.test
index 065c8b6..ceb7ad1 100644
--- a/lldb/test/Shell/SymbolFile/PDB/calling-conventions-x86.test
+++ b/lldb/test/Shell/SymbolFile/PDB/calling-conventions-x86.test
@@ -1,8 +1,12 @@
REQUIRES: system-windows, lld, (target-x86 || target-x86_64)
-RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp \
-RUN: && lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix 32BIT-CHECK %s
-RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t.exe %S/Inputs/CallingConventionsTest.cpp \
-RUN: && lldb-test symbols -dump-ast %t.exe | FileCheck --check-prefix 64BIT-CHECK %s
+
+RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t-32.exe %S/Inputs/CallingConventionsTest.cpp
+RUN: lldb-test symbols -dump-ast %t-32.exe | FileCheck --check-prefix 32BIT-CHECK %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t-32.exe | FileCheck --check-prefix 32BIT-CHECK %s
+
+RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t-64.exe %S/Inputs/CallingConventionsTest.cpp
+RUN: lldb-test symbols -dump-ast %t-64.exe | FileCheck --check-prefix 64BIT-CHECK %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols -dump-ast %t-64.exe | FileCheck --check-prefix 64BIT-CHECK %s
64BIT-CHECK: Module: {{.*}}
64BIT-CHECK-DAG: int (*FuncCCallPtr)();
diff --git a/lldb/test/Shell/SymbolFile/PDB/class-layout.test b/lldb/test/Shell/SymbolFile/PDB/class-layout.test
index e9a7d1c..eca910e 100644
--- a/lldb/test/Shell/SymbolFile/PDB/class-layout.test
+++ b/lldb/test/Shell/SymbolFile/PDB/class-layout.test
@@ -12,9 +12,19 @@ RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=BASE %s
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=FRIEND %s
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=CLASS %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNION %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=STRUCT %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=COMPLEX %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=LIST %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNNAMED-STRUCT %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=BASE %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=FRIEND %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=CLASS %s
CHECK: Module [[MOD:.*]]
-CHECK: SymbolFile pdb ([[MOD]])
+CHECK: SymbolFile {{(native-)?}}pdb ([[MOD]])
CHECK: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\ClassLayoutTest.cpp'
ENUM: name = "Enum", size = 4, decl = ClassLayoutTest.cpp:5
diff --git a/lldb/test/Shell/SymbolFile/PDB/enums-layout.test b/lldb/test/Shell/SymbolFile/PDB/enums-layout.test
index 6f861c6d..9766d6f 100644
--- a/lldb/test/Shell/SymbolFile/PDB/enums-layout.test
+++ b/lldb/test/Shell/SymbolFile/PDB/enums-layout.test
@@ -7,6 +7,12 @@ RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=UCHAR-ENUM %s
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CLASS-ENUM %s
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=STRUCT-ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CONST-ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=EMPTY-ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=UCHAR-ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CLASS-ENUM %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=STRUCT-ENUM %s
; FIXME: PDB does not have information about scoped enumeration (Enum class) so the
; compiler type used is the same as the one for unscoped enumeration.
diff --git a/lldb/test/Shell/SymbolFile/PDB/vbases.test b/lldb/test/Shell/SymbolFile/PDB/vbases.test
index b58e3ed..e5ab80b 100644
--- a/lldb/test/Shell/SymbolFile/PDB/vbases.test
+++ b/lldb/test/Shell/SymbolFile/PDB/vbases.test
@@ -1,6 +1,7 @@
REQUIRES: target-windows, lld
RUN: %build --compiler=clang-cl --output=%t.exe %S/Inputs/VBases.cpp
RUN: %lldb -b -s %S/Inputs/VBases.script -- %t.exe | FileCheck %s
+RUN: env LLDB_USE_NATIVE_PDB_READER=1 %lldb -b -s %S/Inputs/VBases.script -- %t.exe | FileCheck %s
CHECK: {
CHECK: A = (a = '\x01')
diff --git a/lldb/test/Shell/Symtab/Inputs/simple.wasm.yaml b/lldb/test/Shell/Symtab/Inputs/simple.wasm.yaml
new file mode 100644
index 0000000..088d616
--- /dev/null
+++ b/lldb/test/Shell/Symtab/Inputs/simple.wasm.yaml
@@ -0,0 +1,254 @@
+# clang -target wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -O0 -g -o simple.wasm simple.c
+# char* str = "data str";
+#
+# int add(int a, int b) {
+# return a + b;
+# }
+#
+# int main() {
+# int i = 1;
+# int j = 2;
+# return add(i, j);
+# }
+--- !WASM
+FileHeader:
+ Version: 0x1
+Sections:
+ - Type: TYPE
+ Signatures:
+ - Index: 0
+ ParamTypes: []
+ ReturnTypes: []
+ - Index: 1
+ ParamTypes:
+ - I32
+ - I32
+ ReturnTypes:
+ - I32
+ - Index: 2
+ ParamTypes: []
+ ReturnTypes:
+ - I32
+ - Type: FUNCTION
+ FunctionTypes: [ 0, 1, 2, 1 ]
+ - Type: TABLE
+ Tables:
+ - Index: 0
+ ElemType: FUNCREF
+ Limits:
+ Flags: [ HAS_MAX ]
+ Minimum: 0x1
+ Maximum: 0x1
+ - Type: MEMORY
+ Memories:
+ - Minimum: 0x2
+ - Type: GLOBAL
+ Globals:
+ - Index: 0
+ Type: I32
+ Mutable: true
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 66576
+ - Index: 1
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1036
+ - Index: 2
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1024
+ - Index: 3
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1040
+ - Index: 4
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1040
+ - Index: 5
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 66576
+ - Index: 6
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1024
+ - Index: 7
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 66576
+ - Index: 8
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 131072
+ - Index: 9
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 0
+ - Index: 10
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 1
+ - Index: 11
+ Type: I32
+ Mutable: false
+ InitExpr:
+ Opcode: I32_CONST
+ Value: 65536
+ - Type: EXPORT
+ Exports:
+ - Name: memory
+ Kind: MEMORY
+ Index: 0
+ - Name: __wasm_call_ctors
+ Kind: FUNCTION
+ Index: 0
+ - Name: add
+ Kind: FUNCTION
+ Index: 1
+ - Name: __original_main
+ Kind: FUNCTION
+ Index: 2
+ - Name: main
+ Kind: FUNCTION
+ Index: 3
+ - Name: str
+ Kind: GLOBAL
+ Index: 1
+ - Name: __main_void
+ Kind: FUNCTION
+ Index: 2
+ - Name: __indirect_function_table
+ Kind: TABLE
+ Index: 0
+ - Name: __dso_handle
+ Kind: GLOBAL
+ Index: 2
+ - Name: __data_end
+ Kind: GLOBAL
+ Index: 3
+ - Name: __stack_low
+ Kind: GLOBAL
+ Index: 4
+ - Name: __stack_high
+ Kind: GLOBAL
+ Index: 5
+ - Name: __global_base
+ Kind: GLOBAL
+ Index: 6
+ - Name: __heap_base
+ Kind: GLOBAL
+ Index: 7
+ - Name: __heap_end
+ Kind: GLOBAL
+ Index: 8
+ - Name: __memory_base
+ Kind: GLOBAL
+ Index: 9
+ - Name: __table_base
+ Kind: GLOBAL
+ Index: 10
+ - Name: __wasm_first_page_end
+ Kind: GLOBAL
+ Index: 11
+ - Type: CODE
+ Functions:
+ - Index: 0
+ Locals: []
+ Body: 0B
+ - Index: 1
+ Locals:
+ - Type: I32
+ Count: 1
+ Body: 23808080800041106B21022002200036020C20022001360208200228020C20022802086A0F0B
+ - Index: 2
+ Locals:
+ - Type: I32
+ Count: 2
+ Body: 23808080800041106B210020002480808080002000410036020C2000410136020820004102360204200028020820002802041081808080002101200041106A24808080800020010F0B
+ - Index: 3
+ Locals: []
+ Body: 1082808080000F0B
+ - Type: DATA
+ Segments:
+ - SectionOffset: 7
+ InitFlags: 0
+ Offset:
+ Opcode: I32_CONST
+ Value: 1024
+ Content: '646174612073747200'
+ - SectionOffset: 22
+ InitFlags: 0
+ Offset:
+ Opcode: I32_CONST
+ Value: 1036
+ Content: '00040000'
+ - Type: CUSTOM
+ Name: name
+ FunctionNames:
+ - Index: 0
+ Name: __wasm_call_ctors
+ - Index: 1
+ Name: add
+ - Index: 2
+ Name: __original_main
+ - Index: 3
+ Name: main
+ GlobalNames:
+ - Index: 0
+ Name: __stack_pointer
+ DataSegmentNames:
+ - Index: 0
+ Name: .rodata
+ - Index: 1
+ Name: .data
+ - Type: CUSTOM
+ HeaderSecSizeEncodingLen: 2
+ Name: producers
+ Languages:
+ - Name: C11
+ Version: ''
+ Tools:
+ - Name: clang
+ Version: '22.0.0git'
+ - Type: CUSTOM
+ Name: target_features
+ Features:
+ - Prefix: USED
+ Name: bulk-memory
+ - Prefix: USED
+ Name: bulk-memory-opt
+ - Prefix: USED
+ Name: call-indirect-overlong
+ - Prefix: USED
+ Name: multivalue
+ - Prefix: USED
+ Name: mutable-globals
+ - Prefix: USED
+ Name: nontrapping-fptoint
+ - Prefix: USED
+ Name: reference-types
+ - Prefix: USED
+ Name: sign-ext
+...
diff --git a/lldb/test/Shell/Symtab/symtab-wasm.test b/lldb/test/Shell/Symtab/symtab-wasm.test
new file mode 100644
index 0000000..524691b
--- /dev/null
+++ b/lldb/test/Shell/Symtab/symtab-wasm.test
@@ -0,0 +1,16 @@
+# RUN: yaml2obj %S/Inputs/simple.wasm.yaml -o %t.wasm
+
+# 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
+
+# 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/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt
index e2f0395..4a0d2f6 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()
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
index fd2ac64..8ae9d4d 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 6ee1466..e30e02a9 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/debugserver.cpp b/lldb/tools/debugserver/source/debugserver.cpp
index f41a9e0..2c32fe9 100644
--- a/lldb/tools/debugserver/source/debugserver.cpp
+++ b/lldb/tools/debugserver/source/debugserver.cpp
@@ -792,6 +792,39 @@ void show_usage_and_exit(int exit_code) {
DEBUGSERVER_PROGRAM_NAME);
RNBLogSTDERR(" %s /path/file --attach=<process_name>\n",
DEBUGSERVER_PROGRAM_NAME);
+ RNBLogSTDERR("\n");
+ RNBLogSTDERR(" -a | --attach <pid>\n");
+ RNBLogSTDERR(" -w | --waitfor <name>\n");
+ RNBLogSTDERR(" -A | --arch <arch>\n");
+ RNBLogSTDERR(" -g | --debug\n");
+ RNBLogSTDERR(" -K | --kill-on-error\n");
+ RNBLogSTDERR(" -v | --verbose\n");
+ RNBLogSTDERR(" -V | --version\n");
+ RNBLogSTDERR(" -k | --lockdown\n");
+ RNBLogSTDERR(" -t | --applist\n");
+ RNBLogSTDERR(" -l | --log-file\n");
+ RNBLogSTDERR(" -f | --log-flags\n");
+ RNBLogSTDERR(" -x | --launch <auto|posix-spawn|fork-exec|springboard>\n");
+ RNBLogSTDERR(" -d | --waitfor-duration <seconds>\n");
+ RNBLogSTDERR(" -i | --waitfor-interval <usecs>\n");
+ RNBLogSTDERR(" -r | --native-regs\n");
+ RNBLogSTDERR(" -s | --studio-path <path>\n");
+ RNBLogSTDERR(" -I | --stdin-path <path>\n");
+ RNBLogSTDERR(" -O | --stdout-path <path>\n");
+ RNBLogSTDERR(" -E | --stderr-path <path>\n");
+ RNBLogSTDERR(" -n | --no-stdio\n");
+ RNBLogSTDERR(" -S | --setsid\n");
+ RNBLogSTDERR(" -D | --disable-aslr\n");
+ RNBLogSTDERR(" -W | --working-dir <dir>\n");
+ RNBLogSTDERR(" -p | --platform <arg?>\n");
+ RNBLogSTDERR(" -u | --unix-socket <unix socket name>\n");
+ RNBLogSTDERR(" -2 | --fd <file descriptor number>\n");
+ RNBLogSTDERR(" -P | --named-pipe <pipe>\n");
+ RNBLogSTDERR(" -R | --reverse-connect\n");
+ RNBLogSTDERR(" -e | --env <env>\n");
+ RNBLogSTDERR(" -F | --forward-env <env>\n");
+ RNBLogSTDERR(" -U | --unmask-signals\n");
+
exit(exit_code);
}
diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt
index 5c0cac4..446bf68 100644
--- a/lldb/tools/driver/CMakeLists.txt
+++ b/lldb/tools/driver/CMakeLists.txt
@@ -11,7 +11,7 @@ if(APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_BINARY_DIR}/lldb-Info.plist")
endif()
-if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX")
+if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
remove_definitions("-D_XOPEN_SOURCE=700")
add_definitions("-D_ALL_SOURCE")
endif()
diff --git a/lldb/tools/driver/Options.td b/lldb/tools/driver/Options.td
index 1d8372c..311e260 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/Breakpoint.cpp b/lldb/tools/lldb-dap/Breakpoint.cpp
index b4e593e..c803957 100644
--- a/lldb/tools/lldb-dap/Breakpoint.cpp
+++ b/lldb/tools/lldb-dap/Breakpoint.cpp
@@ -8,10 +8,14 @@
#include "Breakpoint.h"
#include "DAP.h"
+#include "LLDBUtils.h"
+#include "Protocol/DAPTypes.h"
#include "ProtocolUtils.h"
#include "lldb/API/SBAddress.h"
#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBLineEntry.h"
+#include "lldb/API/SBModule.h"
#include "lldb/API/SBMutex.h"
#include "llvm/ADT/StringExtras.h"
#include <cstddef>
@@ -21,6 +25,22 @@
using namespace lldb_dap;
+static std::optional<protocol::PersistenceData>
+GetPersistenceDataForSymbol(lldb::SBSymbol &symbol) {
+ protocol::PersistenceData persistence_data;
+ lldb::SBModule module = symbol.GetStartAddress().GetModule();
+ if (!module.IsValid())
+ return std::nullopt;
+
+ lldb::SBFileSpec file_spec = module.GetFileSpec();
+ if (!file_spec.IsValid())
+ return std::nullopt;
+
+ persistence_data.module_path = GetSBFileSpecPath(file_spec);
+ persistence_data.symbol_name = symbol.GetName();
+ return persistence_data;
+}
+
void Breakpoint::SetCondition() { m_bp.SetCondition(m_condition.c_str()); }
void Breakpoint::SetHitCondition() {
@@ -73,7 +93,7 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
const auto column = line_entry.GetColumn();
if (column != LLDB_INVALID_COLUMN_NUMBER)
breakpoint.column = column;
- } else {
+ } else if (source) {
// Assembly breakpoint.
auto symbol = bp_addr.GetSymbol();
if (symbol.IsValid()) {
@@ -82,6 +102,15 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
.ReadInstructions(symbol.GetStartAddress(), bp_addr, nullptr)
.GetSize() +
1;
+
+ // Add persistent data so that the breakpoint can be resolved
+ // in future sessions.
+ std::optional<protocol::PersistenceData> persistence_data =
+ GetPersistenceDataForSymbol(symbol);
+ if (persistence_data) {
+ source->adapterData =
+ protocol::SourceLLDBData{std::move(persistence_data)};
+ }
}
}
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index 4cddfb1..7db334c 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -45,6 +45,7 @@ add_lldb_library(lldbDAP
Handler/LaunchRequestHandler.cpp
Handler/LocationsRequestHandler.cpp
Handler/ModulesRequestHandler.cpp
+ Handler/ModuleSymbolsRequestHandler.cpp
Handler/NextRequestHandler.cpp
Handler/PauseRequestHandler.cpp
Handler/ReadMemoryRequestHandler.cpp
@@ -66,7 +67,8 @@ add_lldb_library(lldbDAP
Handler/ThreadsRequestHandler.cpp
Handler/VariablesRequestHandler.cpp
Handler/WriteMemoryRequestHandler.cpp
-
+
+ Protocol/DAPTypes.cpp
Protocol/ProtocolBase.cpp
Protocol/ProtocolEvents.cpp
Protocol/ProtocolTypes.cpp
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index cbd3b14..b1ad38d 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -23,13 +23,14 @@
#include "Transport.h"
#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBCommandInterpreter.h"
-#include "lldb/API/SBCommandReturnObject.h"
#include "lldb/API/SBEvent.h"
#include "lldb/API/SBLanguageRuntime.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBProcess.h"
#include "lldb/API/SBStream.h"
-#include "lldb/Utility/IOObject.h"
+#include "lldb/Host/JSONTransport.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
@@ -52,7 +53,7 @@
#include <cstdarg>
#include <cstdint>
#include <cstdio>
-#include <fstream>
+#include <functional>
#include <future>
#include <memory>
#include <mutex>
@@ -120,11 +121,12 @@ 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, Transport &transport)
+ std::vector<std::string> pre_init_commands,
+ 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) {
+ repl_mode(default_repl_mode), m_client_name(client_name), m_loop(loop) {
configuration.preInitCommands = std::move(pre_init_commands);
RegisterRequests();
}
@@ -257,36 +259,49 @@ void DAP::SendJSON(const llvm::json::Value &json) {
llvm::json::Path::Root root;
if (!fromJSON(json, message, root)) {
DAP_LOG_ERROR(log, root.getError(), "({1}) encoding failed: {0}",
- transport.GetClientName());
+ m_client_name);
return;
}
Send(message);
}
void DAP::Send(const Message &message) {
- // FIXME: After all the requests have migrated from LegacyRequestHandler >
- // RequestHandler<> this should be handled in RequestHandler<>::operator().
- if (auto *resp = std::get_if<Response>(&message);
- resp && debugger.InterruptRequested()) {
- // Clear the interrupt request.
- debugger.CancelInterruptRequest();
-
- // If the debugger was interrupted, convert this response into a 'cancelled'
- // response because we might have a partial result.
- Response cancelled{/*request_seq=*/resp->request_seq,
- /*command=*/resp->command,
- /*success=*/false,
- /*message=*/eResponseMessageCancelled,
- /*body=*/std::nullopt};
- if (llvm::Error err = transport.Write(cancelled))
- DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
- transport.GetClientName());
+ if (const protocol::Event *event = std::get_if<protocol::Event>(&message)) {
+ if (llvm::Error err = transport.Send(*event))
+ DAP_LOG_ERROR(log, std::move(err), "({0}) sending event failed",
+ m_client_name);
return;
}
- if (llvm::Error err = transport.Write(message))
- DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
- transport.GetClientName());
+ if (const Request *req = std::get_if<Request>(&message)) {
+ if (llvm::Error err = transport.Send(*req))
+ DAP_LOG_ERROR(log, std::move(err), "({0}) sending request failed",
+ m_client_name);
+ return;
+ }
+
+ if (const Response *resp = std::get_if<Response>(&message)) {
+ // FIXME: After all the requests have migrated from LegacyRequestHandler >
+ // RequestHandler<> this should be handled in RequestHandler<>::operator().
+ // If the debugger was interrupted, convert this response into a
+ // 'cancelled' response because we might have a partial result.
+ llvm::Error err =
+ (debugger.InterruptRequested())
+ ? transport.Send({/*request_seq=*/resp->request_seq,
+ /*command=*/resp->command,
+ /*success=*/false,
+ /*message=*/eResponseMessageCancelled,
+ /*body=*/std::nullopt})
+ : transport.Send(*resp);
+ if (err) {
+ DAP_LOG_ERROR(log, std::move(err), "({0}) sending response failed",
+ m_client_name);
+ return;
+ }
+ return;
+ }
+
+ llvm_unreachable("Unexpected message type");
}
// "OutputEvent": {
@@ -551,6 +566,9 @@ lldb::SBThread DAP::GetLLDBThread(const llvm::json::Object &arguments) {
}
lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
+ if (frame_id == LLDB_DAP_INVALID_FRAME_ID)
+ return lldb::SBFrame();
+
lldb::SBProcess process = target.GetProcess();
// Upper 32 bits is the thread index ID
lldb::SBThread thread =
@@ -560,8 +578,8 @@ lldb::SBFrame DAP::GetLLDBFrame(uint64_t frame_id) {
}
lldb::SBFrame DAP::GetLLDBFrame(const llvm::json::Object &arguments) {
- const auto frame_id =
- GetInteger<uint64_t>(arguments, "frameId").value_or(UINT64_MAX);
+ const auto frame_id = GetInteger<uint64_t>(arguments, "frameId")
+ .value_or(LLDB_DAP_INVALID_FRAME_ID);
return GetLLDBFrame(frame_id);
}
@@ -754,7 +772,6 @@ void DAP::RunTerminateCommands() {
}
lldb::SBTarget DAP::CreateTarget(lldb::SBError &error) {
- // Grab the name of the program we need to debug and create a target using
// the given program as an argument. Executable file can be a source of target
// architecture and platform, if they differ from the host. Setting exe path
// in launch info is useless because Target.Launch() will not change
@@ -794,7 +811,7 @@ void DAP::SetTarget(const lldb::SBTarget target) {
bool DAP::HandleObject(const Message &M) {
TelemetryDispatcher dispatcher(&debugger);
- dispatcher.Set("client_name", transport.GetClientName().str());
+ dispatcher.Set("client_name", m_client_name.str());
if (const auto *req = std::get_if<Request>(&M)) {
{
std::lock_guard<std::mutex> guard(m_active_request_mutex);
@@ -820,8 +837,8 @@ bool DAP::HandleObject(const Message &M) {
dispatcher.Set("error",
llvm::Twine("unhandled-command:" + req->command).str());
- DAP_LOG(log, "({0}) error: unhandled command '{1}'",
- transport.GetClientName(), req->command);
+ DAP_LOG(log, "({0}) error: unhandled command '{1}'", m_client_name,
+ req->command);
return false; // Fail
}
@@ -917,9 +934,7 @@ llvm::Error DAP::Disconnect(bool terminateDebuggee) {
}
SendTerminatedEvent();
-
- disconnecting = true;
-
+ TerminateLoop();
return ToError(error);
}
@@ -935,91 +950,121 @@ void DAP::ClearCancelRequest(const CancelArguments &args) {
}
template <typename T>
-static std::optional<T> getArgumentsIfRequest(const Message &pm,
+static std::optional<T> getArgumentsIfRequest(const Request &req,
llvm::StringLiteral command) {
- auto *const req = std::get_if<Request>(&pm);
- if (!req || req->command != command)
+ if (req.command != command)
return std::nullopt;
T args;
llvm::json::Path::Root root;
- if (!fromJSON(req->arguments, args, root))
+ if (!fromJSON(req.arguments, args, root))
return std::nullopt;
return args;
}
-llvm::Error DAP::Loop() {
- // Can't use \a std::future<llvm::Error> because it doesn't compile on
- // Windows.
- std::future<lldb::SBError> queue_reader =
- std::async(std::launch::async, [&]() -> lldb::SBError {
- llvm::set_thread_name(transport.GetClientName() + ".transport_handler");
- auto cleanup = llvm::make_scope_exit([&]() {
- // Ensure we're marked as disconnecting when the reader exits.
- disconnecting = true;
- m_queue_cv.notify_all();
- });
-
- while (!disconnecting) {
- llvm::Expected<Message> next =
- transport.Read<protocol::Message>(std::chrono::seconds(1));
- if (next.errorIsA<TransportEOFError>()) {
- consumeError(next.takeError());
- break;
- }
+void DAP::Received(const protocol::Event &event) {
+ // no-op, no supported events from the client to the server as of DAP v1.68.
+}
- // If the read timed out, continue to check if we should disconnect.
- if (next.errorIsA<TransportTimeoutError>()) {
- consumeError(next.takeError());
- continue;
- }
+void DAP::Received(const protocol::Request &request) {
+ if (request.command == "disconnect")
+ m_disconnecting = true;
- if (llvm::Error err = next.takeError()) {
- lldb::SBError errWrapper;
- errWrapper.SetErrorString(llvm::toString(std::move(err)).c_str());
- return errWrapper;
- }
+ const std::optional<CancelArguments> cancel_args =
+ getArgumentsIfRequest<CancelArguments>(request, "cancel");
+ if (cancel_args) {
+ {
+ std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);
+ if (cancel_args->requestId)
+ m_cancelled_requests.insert(*cancel_args->requestId);
+ }
- if (const protocol::Request *req =
- std::get_if<protocol::Request>(&*next);
- req && req->command == "disconnect")
- disconnecting = true;
-
- const std::optional<CancelArguments> cancel_args =
- getArgumentsIfRequest<CancelArguments>(*next, "cancel");
- if (cancel_args) {
- {
- std::lock_guard<std::mutex> guard(m_cancelled_requests_mutex);
- if (cancel_args->requestId)
- m_cancelled_requests.insert(*cancel_args->requestId);
- }
+ // If a cancel is requested for the active request, make a best
+ // effort attempt to interrupt.
+ std::lock_guard<std::mutex> guard(m_active_request_mutex);
+ if (m_active_request && cancel_args->requestId == m_active_request->seq) {
+ DAP_LOG(log, "({0}) interrupting inflight request (command={1} seq={2})",
+ m_client_name, m_active_request->command, m_active_request->seq);
+ debugger.RequestInterrupt();
+ }
+ }
- // If a cancel is requested for the active request, make a best
- // effort attempt to interrupt.
- std::lock_guard<std::mutex> guard(m_active_request_mutex);
- if (m_active_request &&
- cancel_args->requestId == m_active_request->seq) {
- DAP_LOG(
- log,
- "({0}) interrupting inflight request (command={1} seq={2})",
- transport.GetClientName(), m_active_request->command,
- m_active_request->seq);
- debugger.RequestInterrupt();
- }
- }
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name,
+ request.command, request.seq);
+ m_queue.push_back(request);
+ m_queue_cv.notify_one();
+}
- {
- std::lock_guard<std::mutex> guard(m_queue_mutex);
- m_queue.push_back(std::move(*next));
- }
- m_queue_cv.notify_one();
- }
+void DAP::Received(const protocol::Response &response) {
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ DAP_LOG(log, "({0}) queued (command={1} seq={2})", m_client_name,
+ response.command, response.request_seq);
+ m_queue.push_back(response);
+ m_queue_cv.notify_one();
+}
+
+void DAP::OnError(llvm::Error error) {
+ DAP_LOG_ERROR(log, std::move(error), "({1}) received error: {0}",
+ m_client_name);
+ TerminateLoop(/*failed=*/true);
+}
+
+void DAP::OnClosed() {
+ DAP_LOG(log, "({0}) received EOF", m_client_name);
+ TerminateLoop();
+}
- return lldb::SBError();
- });
+void DAP::TerminateLoop(bool failed) {
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ if (m_disconnecting)
+ return; // Already disconnecting.
- auto cleanup = llvm::make_scope_exit([&]() {
+ m_error_occurred = failed;
+ m_disconnecting = true;
+ m_loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+}
+
+void DAP::TransportHandler() {
+ auto scope_guard = llvm::make_scope_exit([this] {
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ // Ensure we're marked as disconnecting when the reader exits.
+ m_disconnecting = true;
+ m_queue_cv.notify_all();
+ });
+
+ auto handle = transport.RegisterMessageHandler(m_loop, *this);
+ if (!handle) {
+ DAP_LOG_ERROR(log, handle.takeError(),
+ "({1}) registering message handler failed: {0}",
+ m_client_name);
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ m_error_occurred = true;
+ return;
+ }
+
+ if (Status status = m_loop.Run(); status.Fail()) {
+ DAP_LOG_ERROR(log, status.takeError(), "({1}) MainLoop run failed: {0}",
+ m_client_name);
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ m_error_occurred = true;
+ return;
+ }
+}
+
+llvm::Error DAP::Loop() {
+ {
+ // Reset disconnect flag once we start the loop.
+ std::lock_guard<std::mutex> guard(m_queue_mutex);
+ m_disconnecting = false;
+ }
+
+ auto thread = std::thread(std::bind(&DAP::TransportHandler, this));
+
+ auto cleanup = llvm::make_scope_exit([this]() {
+ // FIXME: Merge these into the MainLoop handler.
out.Stop();
err.Stop();
StopEventHandlers();
@@ -1027,9 +1072,9 @@ llvm::Error DAP::Loop() {
while (true) {
std::unique_lock<std::mutex> lock(m_queue_mutex);
- m_queue_cv.wait(lock, [&] { return disconnecting || !m_queue.empty(); });
+ m_queue_cv.wait(lock, [&] { return m_disconnecting || !m_queue.empty(); });
- if (disconnecting && m_queue.empty())
+ if (m_disconnecting && m_queue.empty())
break;
Message next = m_queue.front();
@@ -1043,7 +1088,15 @@ llvm::Error DAP::Loop() {
"unhandled packet");
}
- return ToError(queue_reader.get());
+ m_loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ thread.join();
+
+ if (m_error_occurred)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "DAP Loop terminated due to an internal "
+ "error, see DAP Logs for more information.");
+ return llvm::Error::success();
}
lldb::SBError DAP::WaitForProcessToStop(std::chrono::seconds seconds) {
@@ -1208,6 +1261,27 @@ protocol::Capabilities DAP::GetCapabilities() {
return capabilities;
}
+protocol::Capabilities DAP::GetCustomCapabilities() {
+ protocol::Capabilities capabilities;
+
+ // Add all custom capabilities here.
+ const llvm::DenseSet<AdapterFeature> all_custom_features = {
+ protocol::eAdapterFeatureSupportsModuleSymbolsRequest,
+ };
+
+ for (auto &kv : request_handlers) {
+ llvm::SmallDenseSet<AdapterFeature, 1> features =
+ kv.second->GetSupportedFeatures();
+
+ for (auto &feature : features) {
+ if (all_custom_features.contains(feature))
+ capabilities.supportedFeatures.insert(feature);
+ }
+ }
+
+ return capabilities;
+}
+
void DAP::StartEventThread() {
event_thread = std::thread(&DAP::EventThread, this);
}
@@ -1282,7 +1356,7 @@ void DAP::ProgressEventThread() {
// them prevent multiple threads from writing simultaneously so no locking
// is required.
void DAP::EventThread() {
- llvm::set_thread_name(transport.GetClientName() + ".event_handler");
+ llvm::set_thread_name("lldb.DAP.client." + m_client_name + ".event_handler");
lldb::SBEvent event;
lldb::SBListener listener = debugger.GetListener();
broadcaster.AddListener(listener, eBroadcastBitStopEventThread);
@@ -1314,7 +1388,7 @@ void DAP::EventThread() {
if (llvm::Error err = SendThreadStoppedEvent(*this))
DAP_LOG_ERROR(log, std::move(err),
"({1}) reporting thread stopped: {0}",
- transport.GetClientName());
+ m_client_name);
}
break;
case lldb::eStateRunning:
@@ -1406,11 +1480,15 @@ void DAP::EventThread() {
// avoids sending paths that should be source mapped. Note that
// CreateBreakpoint doesn't apply source mapping and certain
// implementation ignore the source part of this event anyway.
- llvm::json::Value source_bp = bp.ToProtocolBreakpoint();
- source_bp.getAsObject()->erase("source");
+ protocol::Breakpoint protocol_bp = bp.ToProtocolBreakpoint();
+
+ // "source" is not needed here, unless we add adapter data to be
+ // saved by the client.
+ if (protocol_bp.source && !protocol_bp.source->adapterData)
+ protocol_bp.source = std::nullopt;
llvm::json::Object body;
- body.try_emplace("breakpoint", source_bp);
+ body.try_emplace("breakpoint", protocol_bp);
body.try_emplace("reason", "changed");
llvm::json::Object bp_event = CreateEventObject("breakpoint");
@@ -1491,8 +1569,9 @@ std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints(
protocol::Breakpoint response_breakpoint =
iv->second.ToProtocolBreakpoint();
- response_breakpoint.source = source;
+ if (!response_breakpoint.source)
+ response_breakpoint.source = source;
if (!response_breakpoint.line &&
src_bp.GetLine() != LLDB_INVALID_LINE_NUMBER)
response_breakpoint.line = src_bp.GetLine();
@@ -1559,6 +1638,7 @@ void DAP::RegisterRequests() {
// Custom requests
RegisterRequest<CompileUnitsRequestHandler>();
RegisterRequest<ModulesRequestHandler>();
+ RegisterRequest<ModuleSymbolsRequestHandler>();
// Testing requests
RegisterRequest<TestGetTargetBreakpointsRequestHandler>();
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index af4aaba..04f70f7 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -31,6 +31,8 @@
#include "lldb/API/SBMutex.h"
#include "lldb/API/SBTarget.h"
#include "lldb/API/SBThread.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/Status.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
@@ -76,12 +78,16 @@ enum DAPBroadcasterBits {
enum class ReplMode { Variable = 0, Command, Auto };
-struct DAP {
+using DAPTransport =
+ lldb_private::Transport<protocol::Request, protocol::Response,
+ protocol::Event>;
+
+struct DAP final : private DAPTransport::MessageHandler {
/// Path to the lldb-dap binary itself.
static llvm::StringRef debug_adapter_path;
Log *log;
- Transport &transport;
+ DAPTransport &transport;
lldb::SBFile in;
OutputRedirector out;
OutputRedirector err;
@@ -112,7 +118,6 @@ struct DAP {
/// The focused thread for this DAP session.
lldb::tid_t focus_tid = LLDB_INVALID_THREAD_ID;
- bool disconnecting = false;
llvm::once_flag terminated_event_flag;
bool stop_at_entry = false;
bool is_attach = false;
@@ -175,8 +180,11 @@ struct DAP {
/// allocated.
/// \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, Transport &transport);
+ std::vector<std::string> pre_init_commands, llvm::StringRef client_name,
+ DAPTransport &transport, lldb_private::MainLoop &loop);
~DAP();
@@ -315,7 +323,7 @@ struct DAP {
lldb::SBTarget CreateTarget(lldb::SBError &error);
/// Set given target object as a current target for lldb-dap and start
- /// listeing for its breakpoint events.
+ /// listening for its breakpoint events.
void SetTarget(const lldb::SBTarget target);
bool HandleObject(const protocol::Message &M);
@@ -359,6 +367,9 @@ struct DAP {
/// The set of capabilities supported by this adapter.
protocol::Capabilities GetCapabilities();
+ /// The set of custom capabilities supported by this adapter.
+ protocol::Capabilities GetCustomCapabilities();
+
/// Debuggee will continue from stopped state.
void WillContinue() { variables.Clear(); }
@@ -418,12 +429,21 @@ struct DAP {
const std::optional<std::vector<protocol::SourceBreakpoint>>
&breakpoints);
+ void Received(const protocol::Event &) override;
+ void Received(const protocol::Request &) override;
+ void Received(const protocol::Response &) override;
+ void OnError(llvm::Error) override;
+ void OnClosed() override;
+
private:
std::vector<protocol::Breakpoint> SetSourceBreakpoints(
const protocol::Source &source,
const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints,
SourceBreakpointMap &existing_breakpoints);
+ void TransportHandler();
+ void TerminateLoop(bool failed = false);
+
/// Registration of request handler.
/// @{
void RegisterRequests();
@@ -442,6 +462,8 @@ private:
std::thread progress_event_thread;
/// @}
+ const llvm::StringRef m_client_name;
+
/// List of addresses mapped by sourceReference.
std::vector<lldb::addr_t> m_source_references;
std::mutex m_source_references_mutex;
@@ -450,6 +472,11 @@ private:
std::deque<protocol::Message> m_queue;
std::mutex m_queue_mutex;
std::condition_variable m_queue_cv;
+ bool m_disconnecting = false;
+ bool m_error_occurred = false;
+
+ // Loop for managing reading from the client.
+ lldb_private::MainLoop &m_loop;
std::mutex m_cancelled_requests_mutex;
llvm::SmallSet<int64_t, 4> m_cancelled_requests;
diff --git a/lldb/tools/lldb-dap/EventHelper.cpp b/lldb/tools/lldb-dap/EventHelper.cpp
index 364cc7a..bfb05a3 100644
--- a/lldb/tools/lldb-dap/EventHelper.cpp
+++ b/lldb/tools/lldb-dap/EventHelper.cpp
@@ -38,25 +38,37 @@ static void SendThreadExitedEvent(DAP &dap, lldb::tid_t tid) {
dap.SendJSON(llvm::json::Value(std::move(event)));
}
-void SendTargetBasedCapabilities(DAP &dap) {
+/// Get capabilities based on the configured target.
+static llvm::DenseSet<AdapterFeature> GetTargetBasedCapabilities(DAP &dap) {
+ llvm::DenseSet<AdapterFeature> capabilities;
if (!dap.target.IsValid())
- return;
-
- protocol::CapabilitiesEventBody body;
+ return capabilities;
const llvm::StringRef target_triple = dap.target.GetTriple();
if (target_triple.starts_with("x86"))
- body.capabilities.supportedFeatures.insert(
- protocol::eAdapterFeatureStepInTargetsRequest);
+ capabilities.insert(protocol::eAdapterFeatureStepInTargetsRequest);
// We only support restarting launch requests not attach requests.
if (dap.last_launch_request)
- body.capabilities.supportedFeatures.insert(
- protocol::eAdapterFeatureRestartRequest);
+ capabilities.insert(protocol::eAdapterFeatureRestartRequest);
+
+ return capabilities;
+}
+
+void SendExtraCapabilities(DAP &dap) {
+ protocol::Capabilities capabilities = dap.GetCustomCapabilities();
+ llvm::DenseSet<AdapterFeature> target_capabilities =
+ GetTargetBasedCapabilities(dap);
+
+ capabilities.supportedFeatures.insert(target_capabilities.begin(),
+ target_capabilities.end());
+
+ protocol::CapabilitiesEventBody body;
+ body.capabilities = std::move(capabilities);
// Only notify the client if supportedFeatures changed.
if (!body.capabilities.supportedFeatures.empty())
- dap.Send(protocol::Event{"capabilities", body});
+ dap.Send(protocol::Event{"capabilities", std::move(body)});
}
// "ProcessEvent": {
diff --git a/lldb/tools/lldb-dap/EventHelper.h b/lldb/tools/lldb-dap/EventHelper.h
index 72ad530..592c1b8 100644
--- a/lldb/tools/lldb-dap/EventHelper.h
+++ b/lldb/tools/lldb-dap/EventHelper.h
@@ -17,8 +17,8 @@ struct DAP;
enum LaunchMethod { Launch, Attach, AttachForSuspendedLaunch };
-/// Update capabilities based on the configured target.
-void SendTargetBasedCapabilities(DAP &dap);
+/// Sends target based capabilities and lldb-dap custom capabilities.
+void SendExtraCapabilities(DAP &dap);
void SendProcessEvent(DAP &dap, LaunchMethod launch_method);
diff --git a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
index c72fc56..de9a15d 100644
--- a/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp
@@ -8,156 +8,46 @@
#include "DAP.h"
#include "JSONUtils.h"
+#include "Protocol/ProtocolRequests.h"
+#include "Protocol/ProtocolTypes.h"
#include "RequestHandler.h"
#include "lldb/API/SBStringList.h"
-namespace lldb_dap {
+using namespace llvm;
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
-// "CompletionsRequest": {
-// "allOf": [ { "$ref": "#/definitions/Request" }, {
-// "type": "object",
-// "description": "Returns a list of possible completions for a given caret
-// position and text.\nThe CompletionsRequest may only be called if the
-// 'supportsCompletionsRequest' capability exists and is true.",
-// "properties": {
-// "command": {
-// "type": "string",
-// "enum": [ "completions" ]
-// },
-// "arguments": {
-// "$ref": "#/definitions/CompletionsArguments"
-// }
-// },
-// "required": [ "command", "arguments" ]
-// }]
-// },
-// "CompletionsArguments": {
-// "type": "object",
-// "description": "Arguments for 'completions' request.",
-// "properties": {
-// "frameId": {
-// "type": "integer",
-// "description": "Returns completions in the scope of this stack frame.
-// If not specified, the completions are returned for the global scope."
-// },
-// "text": {
-// "type": "string",
-// "description": "One or more source lines. Typically this is the text a
-// user has typed into the debug console before he asked for completion."
-// },
-// "column": {
-// "type": "integer",
-// "description": "The character position for which to determine the
-// completion proposals."
-// },
-// "line": {
-// "type": "integer",
-// "description": "An optional line for which to determine the completion
-// proposals. If missing the first line of the text is assumed."
-// }
-// },
-// "required": [ "text", "column" ]
-// },
-// "CompletionsResponse": {
-// "allOf": [ { "$ref": "#/definitions/Response" }, {
-// "type": "object",
-// "description": "Response to 'completions' request.",
-// "properties": {
-// "body": {
-// "type": "object",
-// "properties": {
-// "targets": {
-// "type": "array",
-// "items": {
-// "$ref": "#/definitions/CompletionItem"
-// },
-// "description": "The possible completions for ."
-// }
-// },
-// "required": [ "targets" ]
-// }
-// },
-// "required": [ "body" ]
-// }]
-// },
-// "CompletionItem": {
-// "type": "object",
-// "description": "CompletionItems are the suggestions returned from the
-// CompletionsRequest.", "properties": {
-// "label": {
-// "type": "string",
-// "description": "The label of this completion item. By default this is
-// also the text that is inserted when selecting this completion."
-// },
-// "text": {
-// "type": "string",
-// "description": "If text is not falsy then it is inserted instead of the
-// label."
-// },
-// "sortText": {
-// "type": "string",
-// "description": "A string that should be used when comparing this item
-// with other items. When `falsy` the label is used."
-// },
-// "type": {
-// "$ref": "#/definitions/CompletionItemType",
-// "description": "The item's type. Typically the client uses this
-// information to render the item in the UI with an icon."
-// },
-// "start": {
-// "type": "integer",
-// "description": "This value determines the location (in the
-// CompletionsRequest's 'text' attribute) where the completion text is
-// added.\nIf missing the text is added at the location specified by the
-// CompletionsRequest's 'column' attribute."
-// },
-// "length": {
-// "type": "integer",
-// "description": "This value determines how many characters are
-// overwritten by the completion text.\nIf missing the value 0 is assumed
-// which results in the completion text being inserted."
-// }
-// },
-// "required": [ "label" ]
-// },
-// "CompletionItemType": {
-// "type": "string",
-// "description": "Some predefined types for the CompletionItem. Please note
-// that not all clients have specific icons for all of them.", "enum": [
-// "method", "function", "constructor", "field", "variable", "class",
-// "interface", "module", "property", "unit", "value", "enum", "keyword",
-// "snippet", "text", "color", "file", "reference", "customcolor" ]
-// }
-void CompletionsRequestHandler::operator()(
- const llvm::json::Object &request) const {
- llvm::json::Object response;
- FillResponse(request, response);
- llvm::json::Object body;
- const auto *arguments = request.getObject("arguments");
+namespace lldb_dap {
+/// Returns a list of possible completions for a given caret position and text.
+///
+/// Clients should only call this request if the corresponding capability
+/// `supportsCompletionsRequest` is true.
+Expected<CompletionsResponseBody>
+CompletionsRequestHandler::Run(const CompletionsArguments &args) const {
// If we have a frame, try to set the context for variable completions.
- lldb::SBFrame frame = dap.GetLLDBFrame(*arguments);
+ lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
if (frame.IsValid()) {
frame.GetThread().GetProcess().SetSelectedThread(frame.GetThread());
frame.GetThread().SetSelectedFrame(frame.GetFrameID());
}
- std::string text = GetString(arguments, "text").value_or("").str();
- auto original_column =
- GetInteger<int64_t>(arguments, "column").value_or(text.size());
- auto original_line = GetInteger<int64_t>(arguments, "line").value_or(1);
+ std::string text = args.text;
+ auto original_column = args.column;
+ auto original_line = args.line;
auto offset = original_column - 1;
if (original_line > 1) {
- llvm::SmallVector<::llvm::StringRef, 2> lines;
- llvm::StringRef(text).split(lines, '\n');
+ SmallVector<StringRef, 2> lines;
+ StringRef(text).split(lines, '\n');
for (int i = 0; i < original_line - 1; i++) {
offset += lines[i].size();
}
}
- llvm::json::Array targets;
+
+ std::vector<CompletionItem> targets;
bool had_escape_prefix =
- llvm::StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
+ StringRef(text).starts_with(dap.configuration.commandEscapePrefix);
ReplMode completion_mode = dap.DetectReplMode(frame, text, true);
// Handle the offset change introduced by stripping out the
@@ -165,10 +55,7 @@ void CompletionsRequestHandler::operator()(
if (had_escape_prefix) {
if (offset <
static_cast<int64_t>(dap.configuration.commandEscapePrefix.size())) {
- body.try_emplace("targets", std::move(targets));
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
- return;
+ return CompletionsResponseBody{std::move(targets)};
}
offset -= dap.configuration.commandEscapePrefix.size();
}
@@ -198,27 +85,25 @@ void CompletionsRequestHandler::operator()(
std::string match = matches.GetStringAtIndex(i);
std::string description = descriptions.GetStringAtIndex(i);
- llvm::json::Object item;
- llvm::StringRef match_ref = match;
- for (llvm::StringRef commit_point : {".", "->"}) {
+ CompletionItem item;
+ StringRef match_ref = match;
+ for (StringRef commit_point : {".", "->"}) {
if (match_ref.contains(commit_point)) {
match_ref = match_ref.rsplit(commit_point).second;
}
}
- EmplaceSafeString(item, "text", match_ref);
+ item.text = match_ref;
if (description.empty())
- EmplaceSafeString(item, "label", match);
+ item.label = match;
else
- EmplaceSafeString(item, "label", match + " -- " + description);
+ item.label = match + " -- " + description;
targets.emplace_back(std::move(item));
}
}
- body.try_emplace("targets", std::move(targets));
- response.try_emplace("body", std::move(body));
- dap.SendJSON(llvm::json::Value(std::move(response)));
+ return CompletionsResponseBody{std::move(targets)};
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
index e7735a7..1bfe7b7 100644
--- a/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/ConfigurationDoneRequestHandler.cpp
@@ -9,6 +9,7 @@
#include "DAP.h"
#include "EventHelper.h"
#include "LLDBUtils.h"
+#include "Protocol/ProtocolEvents.h"
#include "Protocol/ProtocolRequests.h"
#include "ProtocolUtils.h"
#include "RequestHandler.h"
@@ -44,7 +45,10 @@ ConfigurationDoneRequestHandler::Run(const ConfigurationDoneArguments &) const {
// Waiting until 'configurationDone' to send target based capabilities in case
// the launch or attach scripts adjust the target. The initial dummy target
// may have different capabilities than the final target.
- SendTargetBasedCapabilities(dap);
+
+ /// Also send here custom capabilities to the client, which is consumed by the
+ /// lldb-dap specific editor extension.
+ SendExtraCapabilities(dap);
// Clients can request a baseline of currently existing threads after
// we acknowledge the configurationDone request.
diff --git a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
index 8cb25d0..87b93fc 100644
--- a/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/DataBreakpointInfoRequestHandler.cpp
@@ -23,7 +23,7 @@ llvm::Expected<protocol::DataBreakpointInfoResponseBody>
DataBreakpointInfoRequestHandler::Run(
const protocol::DataBreakpointInfoArguments &args) const {
protocol::DataBreakpointInfoResponseBody response;
- lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId.value_or(UINT64_MAX));
+ lldb::SBFrame frame = dap.GetLLDBFrame(args.frameId);
lldb::SBValue variable = dap.variables.FindVariable(
args.variablesReference.value_or(0), args.name);
std::string addr, size;
diff --git a/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp
new file mode 100644
index 0000000..4a9d256
--- /dev/null
+++ b/lldb/tools/lldb-dap/Handler/ModuleSymbolsRequestHandler.cpp
@@ -0,0 +1,90 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "DAP.h"
+#include "DAPError.h"
+#include "Protocol/DAPTypes.h"
+#include "RequestHandler.h"
+#include "lldb/API/SBAddress.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBModule.h"
+#include "lldb/API/SBModuleSpec.h"
+#include "lldb/Utility/UUID.h"
+#include "llvm/Support/Error.h"
+#include <cstddef>
+
+using namespace lldb_dap::protocol;
+namespace lldb_dap {
+
+llvm::Expected<ModuleSymbolsResponseBody>
+ModuleSymbolsRequestHandler::Run(const ModuleSymbolsArguments &args) const {
+ ModuleSymbolsResponseBody response;
+
+ lldb::SBModuleSpec module_spec;
+ if (!args.moduleId.empty()) {
+ llvm::SmallVector<uint8_t, 20> uuid_bytes;
+ if (!lldb_private::UUID::DecodeUUIDBytesFromString(args.moduleId,
+ uuid_bytes)
+ .empty())
+ return llvm::make_error<DAPError>("invalid module ID");
+
+ module_spec.SetUUIDBytes(uuid_bytes.data(), uuid_bytes.size());
+ }
+
+ if (!args.moduleName.empty()) {
+ lldb::SBFileSpec file_spec;
+ file_spec.SetFilename(args.moduleName.c_str());
+ module_spec.SetFileSpec(file_spec);
+ }
+
+ // Empty request, return empty response.
+ if (!module_spec.IsValid())
+ return response;
+
+ std::vector<Symbol> &symbols = response.symbols;
+ lldb::SBModule module = dap.target.FindModule(module_spec);
+ if (!module.IsValid())
+ return llvm::make_error<DAPError>("module not found");
+
+ const size_t num_symbols = module.GetNumSymbols();
+ const size_t start_index = args.startIndex.value_or(0);
+ const size_t end_index =
+ std::min(start_index + args.count.value_or(num_symbols), num_symbols);
+ for (size_t i = start_index; i < end_index; ++i) {
+ lldb::SBSymbol symbol = module.GetSymbolAtIndex(i);
+ if (!symbol.IsValid())
+ continue;
+
+ Symbol dap_symbol;
+ dap_symbol.id = symbol.GetID();
+ dap_symbol.type = symbol.GetType();
+ dap_symbol.isDebug = symbol.IsDebug();
+ dap_symbol.isSynthetic = symbol.IsSynthetic();
+ dap_symbol.isExternal = symbol.IsExternal();
+
+ lldb::SBAddress start_address = symbol.GetStartAddress();
+ if (start_address.IsValid()) {
+ lldb::addr_t file_address = start_address.GetFileAddress();
+ if (file_address != LLDB_INVALID_ADDRESS)
+ dap_symbol.fileAddress = file_address;
+
+ lldb::addr_t load_address = start_address.GetLoadAddress(dap.target);
+ if (load_address != LLDB_INVALID_ADDRESS)
+ dap_symbol.loadAddress = load_address;
+ }
+
+ dap_symbol.size = symbol.GetSize();
+ if (const char *symbol_name = symbol.GetName())
+ dap_symbol.name = symbol_name;
+ symbols.push_back(std::move(dap_symbol));
+ }
+
+ return response;
+}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.h b/lldb/tools/lldb-dap/Handler/RequestHandler.h
index 16f8062..977a247 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.h
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.h
@@ -243,14 +243,17 @@ public:
uint32_t end_line) const;
};
-class CompletionsRequestHandler : public LegacyRequestHandler {
+class CompletionsRequestHandler
+ : public RequestHandler<protocol::CompletionsArguments,
+ llvm::Expected<protocol::CompletionsResponseBody>> {
public:
- using LegacyRequestHandler::LegacyRequestHandler;
+ using RequestHandler::RequestHandler;
static llvm::StringLiteral GetCommand() { return "completions"; }
FeatureSet GetSupportedFeatures() const override {
return {protocol::eAdapterFeatureCompletionsRequest};
}
- void operator()(const llvm::json::Object &request) const override;
+ llvm::Expected<protocol::CompletionsResponseBody>
+ Run(const protocol::CompletionsArguments &args) const override;
};
class ContinueRequestHandler
@@ -594,6 +597,20 @@ public:
llvm::Error Run(const protocol::CancelArguments &args) const override;
};
+class ModuleSymbolsRequestHandler
+ : public RequestHandler<
+ protocol::ModuleSymbolsArguments,
+ llvm::Expected<protocol::ModuleSymbolsResponseBody>> {
+public:
+ using RequestHandler::RequestHandler;
+ static llvm::StringLiteral GetCommand() { return "__lldb_moduleSymbols"; }
+ FeatureSet GetSupportedFeatures() const override {
+ return {protocol::eAdapterFeatureSupportsModuleSymbolsRequest};
+ }
+ llvm::Expected<protocol::ModuleSymbolsResponseBody>
+ Run(const protocol::ModuleSymbolsArguments &args) const override;
+};
+
/// A request used in testing to get the details on all breakpoints that are
/// currently set in the target. This helps us to test "setBreakpoints" and
/// "setFunctionBreakpoints" requests to verify we have the correct set of
diff --git a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
index 5d336af..142351f 100644
--- a/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/SetBreakpointsRequestHandler.cpp
@@ -9,7 +9,6 @@
#include "DAP.h"
#include "Protocol/ProtocolRequests.h"
#include "RequestHandler.h"
-#include <vector>
namespace lldb_dap {
diff --git a/lldb/tools/lldb-dap/Protocol/DAPTypes.cpp b/lldb/tools/lldb-dap/Protocol/DAPTypes.cpp
new file mode 100644
index 0000000..a14ed9e
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/DAPTypes.cpp
@@ -0,0 +1,69 @@
+#include "Protocol/DAPTypes.h"
+#include "lldb/API/SBSymbol.h"
+#include "lldb/lldb-enumerations.h"
+
+using namespace llvm;
+
+namespace lldb_dap::protocol {
+
+bool fromJSON(const llvm::json::Value &Params, PersistenceData &PD,
+ llvm::json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.mapOptional("module_path", PD.module_path) &&
+ O.mapOptional("symbol_name", PD.symbol_name);
+}
+
+llvm::json::Value toJSON(const PersistenceData &PD) {
+ json::Object result{
+ {"module_path", PD.module_path},
+ {"symbol_name", PD.symbol_name},
+ };
+
+ return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params, SourceLLDBData &SLD,
+ llvm::json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.mapOptional("persistenceData", SLD.persistenceData);
+}
+
+llvm::json::Value toJSON(const SourceLLDBData &SLD) {
+ json::Object result;
+ if (SLD.persistenceData)
+ result.insert({"persistenceData", SLD.persistenceData});
+ return result;
+}
+
+bool fromJSON(const llvm::json::Value &Params, Symbol &DS, llvm::json::Path P) {
+ json::ObjectMapper O(Params, P);
+ std::string type_str;
+ if (!(O && O.map("id", DS.id) && O.map("isDebug", DS.isDebug) &&
+ O.map("isSynthetic", DS.isSynthetic) &&
+ O.map("isExternal", DS.isExternal) && O.map("type", type_str) &&
+ O.map("fileAddress", DS.fileAddress) &&
+ O.mapOptional("loadAddress", DS.loadAddress) &&
+ O.map("size", DS.size) && O.map("name", DS.name)))
+ return false;
+
+ DS.type = lldb::SBSymbol::GetTypeFromString(type_str.c_str());
+ return true;
+}
+
+llvm::json::Value toJSON(const Symbol &DS) {
+ json::Object result{
+ {"id", DS.id},
+ {"isDebug", DS.isDebug},
+ {"isSynthetic", DS.isSynthetic},
+ {"isExternal", DS.isExternal},
+ {"type", lldb::SBSymbol::GetTypeAsString(DS.type)},
+ {"fileAddress", DS.fileAddress},
+ {"loadAddress", DS.loadAddress},
+ {"size", DS.size},
+ {"name", DS.name},
+ };
+
+ return result;
+}
+
+} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/DAPTypes.h b/lldb/tools/lldb-dap/Protocol/DAPTypes.h
new file mode 100644
index 0000000..7fccf13
--- /dev/null
+++ b/lldb/tools/lldb-dap/Protocol/DAPTypes.h
@@ -0,0 +1,85 @@
+//===-- ProtocolTypes.h ---------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains private DAP types used in the protocol.
+//
+// Each struct has a toJSON and fromJSON function, that converts between
+// the struct and a JSON representation. (See JSON.h)
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
+#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_DAP_TYPES_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/Support/JSON.h"
+#include <optional>
+#include <string>
+
+namespace lldb_dap::protocol {
+
+/// Data used to help lldb-dap resolve breakpoints persistently across different
+/// sessions. This information is especially useful for assembly breakpoints,
+/// because `sourceReference` can change across sessions. For regular source
+/// breakpoints the path and line are the same For each session.
+struct PersistenceData {
+ /// The source module path.
+ std::string module_path;
+
+ /// The symbol name of the Source.
+ std::string symbol_name;
+};
+bool fromJSON(const llvm::json::Value &, PersistenceData &, llvm::json::Path);
+llvm::json::Value toJSON(const PersistenceData &);
+
+/// Custom source data used by lldb-dap.
+/// This data should help lldb-dap identify sources correctly across different
+/// sessions.
+struct SourceLLDBData {
+ /// Data that helps lldb resolve this source persistently across different
+ /// sessions.
+ std::optional<PersistenceData> persistenceData;
+};
+bool fromJSON(const llvm::json::Value &, SourceLLDBData &, llvm::json::Path);
+llvm::json::Value toJSON(const SourceLLDBData &);
+
+struct Symbol {
+ /// The symbol id, usually the original symbol table index.
+ uint32_t id;
+
+ /// True if this symbol is debug information in a symbol.
+ bool isDebug;
+
+ /// True if this symbol is not actually in the symbol table, but synthesized
+ /// from other info in the object file.
+ bool isSynthetic;
+
+ /// True if this symbol is globally visible.
+ bool isExternal;
+
+ /// The symbol type.
+ lldb::SymbolType type;
+
+ /// The symbol file address.
+ lldb::addr_t fileAddress;
+
+ /// The symbol load address.
+ std::optional<lldb::addr_t> loadAddress;
+
+ /// The symbol size.
+ lldb::addr_t size;
+
+ /// The symbol name.
+ std::string name;
+};
+bool fromJSON(const llvm::json::Value &, Symbol &, llvm::json::Path);
+llvm::json::Value toJSON(const Symbol &);
+
+} // namespace lldb_dap::protocol
+
+#endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
index bc4fee4..9cd9028 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.cpp
@@ -98,6 +98,10 @@ bool fromJSON(json::Value const &Params, Request &R, json::Path P) {
return mapRaw(Params, "arguments", R.arguments, P);
}
+bool operator==(const Request &a, const Request &b) {
+ return a.seq == b.seq && a.command == b.command && a.arguments == b.arguments;
+}
+
json::Value toJSON(const Response &R) {
json::Object Result{{"type", "response"},
{"seq", 0},
@@ -177,6 +181,11 @@ bool fromJSON(json::Value const &Params, Response &R, json::Path P) {
mapRaw(Params, "body", R.body, P);
}
+bool operator==(const Response &a, const Response &b) {
+ return a.request_seq == b.request_seq && a.command == b.command &&
+ a.success == b.success && a.message == b.message && a.body == b.body;
+}
+
json::Value toJSON(const ErrorMessage &EM) {
json::Object Result{{"id", EM.id}, {"format", EM.format}};
@@ -248,6 +257,10 @@ bool fromJSON(json::Value const &Params, Event &E, json::Path P) {
return mapRaw(Params, "body", E.body, P);
}
+bool operator==(const Event &a, const Event &b) {
+ return a.event == b.event && a.body == b.body;
+}
+
bool fromJSON(const json::Value &Params, Message &PM, json::Path P) {
json::ObjectMapper O(Params, P);
if (!O)
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
index 81496380..0a9ef53 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -52,6 +52,7 @@ struct Request {
};
llvm::json::Value toJSON(const Request &);
bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path);
+bool operator==(const Request &, const Request &);
/// A debug adapter initiated event.
struct Event {
@@ -63,6 +64,7 @@ struct Event {
};
llvm::json::Value toJSON(const Event &);
bool fromJSON(const llvm::json::Value &, Event &, llvm::json::Path);
+bool operator==(const Event &, const Event &);
enum ResponseMessage : unsigned {
/// The request was cancelled
@@ -101,6 +103,7 @@ struct Response {
};
bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path);
llvm::json::Value toJSON(const Response &);
+bool operator==(const Response &, const Response &);
/// A structured message object. Used to return errors from requests.
struct ErrorMessage {
@@ -140,6 +143,7 @@ llvm::json::Value toJSON(const ErrorMessage &);
using Message = std::variant<Request, Response, Event>;
bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path);
llvm::json::Value toJSON(const Message &);
+bool operator==(const Message &, const Message &);
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Message &V) {
OS << toJSON(V);
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index 29855ca..e1806d6 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);
}
@@ -329,6 +329,17 @@ json::Value toJSON(const ContinueResponseBody &CRB) {
return std::move(Body);
}
+bool fromJSON(const json::Value &Params, CompletionsArguments &CA,
+ json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("text", CA.text) && O.map("column", CA.column) &&
+ O.mapOptional("frameId", CA.frameId) && O.mapOptional("line", CA.line);
+}
+
+json::Value toJSON(const CompletionsResponseBody &CRB) {
+ return json::Object{{"targets", CRB.targets}};
+}
+
bool fromJSON(const json::Value &Params, SetVariableArguments &SVA,
json::Path P) {
json::ObjectMapper O(Params, P);
@@ -598,4 +609,19 @@ json::Value toJSON(const WriteMemoryResponseBody &WMR) {
return result;
}
+bool fromJSON(const llvm::json::Value &Params, ModuleSymbolsArguments &Args,
+ llvm::json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("moduleId", Args.moduleId) &&
+ O.map("moduleName", Args.moduleName) &&
+ O.mapOptional("startIndex", Args.startIndex) &&
+ O.mapOptional("count", Args.count);
+}
+
+llvm::json::Value toJSON(const ModuleSymbolsResponseBody &DGMSR) {
+ json::Object result;
+ result.insert({"symbols", DGMSR.symbols});
+ return result;
+}
+
} // namespace lldb_dap::protocol
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index c45ee10..0848ee5 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -310,6 +310,8 @@ bool fromJSON(const llvm::json::Value &, LaunchRequestArguments &,
using LaunchResponse = VoidResponse;
#define LLDB_DAP_INVALID_PORT -1
+/// An invalid 'frameId' default value.
+#define LLDB_DAP_INVALID_FRAME_ID UINT64_MAX
/// lldb-dap specific attach arguments.
struct AttachRequestArguments {
@@ -376,6 +378,35 @@ struct ContinueResponseBody {
};
llvm::json::Value toJSON(const ContinueResponseBody &);
+/// Arguments for `completions` request.
+struct CompletionsArguments {
+ /// Returns completions in the scope of this stack frame. If not specified,
+ /// the completions are returned for the global scope.
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
+
+ /// One or more source lines. Typically this is the text users have typed into
+ /// the debug console before they asked for completion.
+ std::string text;
+
+ /// The position within `text` for which to determine the completion
+ /// proposals. It is measured in UTF-16 code units and the client capability
+ /// `columnsStartAt1` determines whether it is 0- or 1-based.
+ int64_t column = 0;
+
+ /// A line for which to determine the completion proposals. If missing the
+ /// first line of the text is assumed.
+ int64_t line = 0;
+};
+bool fromJSON(const llvm::json::Value &, CompletionsArguments &,
+ llvm::json::Path);
+
+/// Response to `completions` request.
+struct CompletionsResponseBody {
+ /// The possible completions for a given caret position and text.
+ std::vector<CompletionItem> targets;
+};
+llvm::json::Value toJSON(const CompletionsResponseBody &);
+
/// Arguments for `configurationDone` request.
using ConfigurationDoneArguments = EmptyArguments;
@@ -455,7 +486,7 @@ struct ScopesArguments {
/// Retrieve the scopes for the stack frame identified by `frameId`. The
/// `frameId` must have been obtained in the current suspended state. See
/// 'Lifetime of Object References' in the Overview section for details.
- uint64_t frameId = LLDB_INVALID_FRAME_ID;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
};
bool fromJSON(const llvm::json::Value &, ScopesArguments &, llvm::json::Path);
@@ -541,7 +572,7 @@ using StepInResponse = VoidResponse;
/// Arguments for `stepInTargets` request.
struct StepInTargetsArguments {
/// The stack frame for which to retrieve the possible step-in targets.
- uint64_t frameId = LLDB_INVALID_FRAME_ID;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
};
bool fromJSON(const llvm::json::Value &, StepInTargetsArguments &,
llvm::json::Path);
@@ -690,7 +721,7 @@ struct DataBreakpointInfoArguments {
/// When `name` is an expression, evaluate it in the scope of this stack
/// frame. If not specified, the expression is evaluated in the global scope.
/// When `asAddress` is true, the `frameId` is ignored.
- std::optional<uint64_t> frameId;
+ uint64_t frameId = LLDB_DAP_INVALID_FRAME_ID;
/// If specified, a debug adapter should return information for the range of
/// memory extending `bytes` number of bytes from the address or variable
@@ -981,6 +1012,30 @@ struct WriteMemoryResponseBody {
};
llvm::json::Value toJSON(const WriteMemoryResponseBody &);
+struct ModuleSymbolsArguments {
+ /// The module UUID for which to retrieve symbols.
+ std::string moduleId;
+
+ /// The module path.
+ std::string moduleName;
+
+ /// The index of the first symbol to return; if omitted, start at the
+ /// beginning.
+ std::optional<uint32_t> startIndex;
+
+ /// The number of symbols to return; if omitted, all symbols are returned.
+ std::optional<uint32_t> count;
+};
+bool fromJSON(const llvm::json::Value &, ModuleSymbolsArguments &,
+ llvm::json::Path);
+
+/// Response to `getModuleSymbols` request.
+struct ModuleSymbolsResponseBody {
+ /// The symbols for the specified module.
+ std::vector<Symbol> symbols;
+};
+llvm::json::Value toJSON(const ModuleSymbolsResponseBody &);
+
} // namespace lldb_dap::protocol
#endif
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
index fe8bb52..dc8edaa 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp
@@ -46,7 +46,8 @@ bool fromJSON(const json::Value &Params, Source &S, json::Path P) {
json::ObjectMapper O(Params, P);
return O && O.map("name", S.name) && O.map("path", S.path) &&
O.map("presentationHint", S.presentationHint) &&
- O.map("sourceReference", S.sourceReference);
+ O.map("sourceReference", S.sourceReference) &&
+ O.map("adapterData", S.adapterData);
}
llvm::json::Value toJSON(Source::PresentationHint hint) {
@@ -71,6 +72,8 @@ llvm::json::Value toJSON(const Source &S) {
result.insert({"sourceReference", *S.sourceReference});
if (S.presentationHint)
result.insert({"presentationHint", *S.presentationHint});
+ if (S.adapterData)
+ result.insert({"adapterData", *S.adapterData});
return result;
}
@@ -197,6 +200,123 @@ bool fromJSON(const llvm::json::Value &Params, ChecksumAlgorithm &CA,
return true;
}
+bool fromJSON(const json::Value &Params, CompletionItemType &CIT,
+ json::Path P) {
+ auto raw_item_type = Params.getAsString();
+ if (!raw_item_type) {
+ P.report("expected a string");
+ return false;
+ }
+
+ std::optional<CompletionItemType> item_type =
+ StringSwitch<std::optional<CompletionItemType>>(*raw_item_type)
+ .Case("method", eCompletionItemTypeMethod)
+ .Case("function", eCompletionItemTypeFunction)
+ .Case("constructor", eCompletionItemTypeConstructor)
+ .Case("field", eCompletionItemTypeField)
+ .Case("variable", eCompletionItemTypeVariable)
+ .Case("class", eCompletionItemTypeClass)
+ .Case("interface", eCompletionItemTypeInterface)
+ .Case("module", eCompletionItemTypeModule)
+ .Case("property", eCompletionItemTypeProperty)
+ .Case("unit", eCompletionItemTypeUnit)
+ .Case("value", eCompletionItemTypeValue)
+ .Case("enum", eCompletionItemTypeEnum)
+ .Case("keyword", eCompletionItemTypeKeyword)
+ .Case("snippet", eCompletionItemTypeSnippet)
+ .Case("text", eCompletionItemTypeText)
+ .Case("color", eCompletionItemTypeColor)
+ .Case("file", eCompletionItemTypeFile)
+ .Case("reference", eCompletionItemTypeReference)
+ .Case("customcolor", eCompletionItemTypeCustomColor)
+ .Default(std::nullopt);
+
+ if (!item_type) {
+ P.report("unexpected value");
+ return false;
+ }
+
+ CIT = *item_type;
+ return true;
+}
+
+json::Value toJSON(const CompletionItemType &CIT) {
+ switch (CIT) {
+ case eCompletionItemTypeMethod:
+ return "method";
+ case eCompletionItemTypeFunction:
+ return "function";
+ case eCompletionItemTypeConstructor:
+ return "constructor";
+ case eCompletionItemTypeField:
+ return "field";
+ case eCompletionItemTypeVariable:
+ return "variable";
+ case eCompletionItemTypeClass:
+ return "class";
+ case eCompletionItemTypeInterface:
+ return "interface";
+ case eCompletionItemTypeModule:
+ return "module";
+ case eCompletionItemTypeProperty:
+ return "property";
+ case eCompletionItemTypeUnit:
+ return "unit";
+ case eCompletionItemTypeValue:
+ return "value";
+ case eCompletionItemTypeEnum:
+ return "enum";
+ case eCompletionItemTypeKeyword:
+ return "keyword";
+ case eCompletionItemTypeSnippet:
+ return "snippet";
+ case eCompletionItemTypeText:
+ return "text";
+ case eCompletionItemTypeColor:
+ return "color";
+ case eCompletionItemTypeFile:
+ return "file";
+ case eCompletionItemTypeReference:
+ return "reference";
+ case eCompletionItemTypeCustomColor:
+ return "customcolor";
+ }
+ llvm_unreachable("unhandled CompletionItemType.");
+}
+
+bool fromJSON(const json::Value &Params, CompletionItem &CI, json::Path P) {
+ json::ObjectMapper O(Params, P);
+ return O && O.map("label", CI.label) && O.mapOptional("text", CI.text) &&
+ O.mapOptional("sortText", CI.sortText) &&
+ O.mapOptional("detail", CI.detail) && O.mapOptional("type", CI.type) &&
+ O.mapOptional("start", CI.start) &&
+ O.mapOptional("length", CI.length) &&
+ O.mapOptional("selectionStart", CI.selectionStart) &&
+ O.mapOptional("selectionLength", CI.selectionLength);
+}
+json::Value toJSON(const CompletionItem &CI) {
+ json::Object result{{"label", CI.label}};
+
+ if (!CI.text.empty())
+ result.insert({"text", CI.text});
+ if (!CI.sortText.empty())
+ result.insert({"sortText", CI.sortText});
+ if (!CI.detail.empty())
+ result.insert({"detail", CI.detail});
+ if (CI.type)
+ result.insert({"type", CI.type});
+ if (CI.start)
+ result.insert({"start", CI.start});
+ if (CI.length)
+ result.insert({"length", CI.length});
+ if (CI.selectionStart)
+ result.insert({"selectionStart", CI.selectionStart});
+ if (CI.selectionLength)
+ result.insert({"selectionLength", CI.selectionLength});
+
+ return result;
+}
+
json::Value toJSON(const BreakpointModeApplicability &BMA) {
switch (BMA) {
case eBreakpointModeApplicabilitySource:
@@ -332,6 +452,8 @@ static llvm::StringLiteral ToString(AdapterFeature feature) {
return "supportsWriteMemoryRequest";
case eAdapterFeatureTerminateDebuggee:
return "supportTerminateDebuggee";
+ case eAdapterFeatureSupportsModuleSymbolsRequest:
+ return "supportsModuleSymbolsRequest";
}
llvm_unreachable("unhandled adapter feature.");
}
@@ -403,6 +525,8 @@ bool fromJSON(const llvm::json::Value &Params, AdapterFeature &feature,
eAdapterFeatureValueFormattingOptions)
.Case("supportsWriteMemoryRequest", eAdapterFeatureWriteMemoryRequest)
.Case("supportTerminateDebuggee", eAdapterFeatureTerminateDebuggee)
+ .Case("supportsModuleSymbolsRequest",
+ eAdapterFeatureSupportsModuleSymbolsRequest)
.Default(std::nullopt);
if (!parsedFeature) {
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
index 89122c8..7077df9 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolTypes.h
@@ -20,6 +20,7 @@
#ifndef LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
#define LLDB_TOOLS_LLDB_DAP_PROTOCOL_PROTOCOL_TYPES_H
+#include "Protocol/DAPTypes.h"
#include "lldb/lldb-defines.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/JSON.h"
@@ -108,6 +109,84 @@ enum ChecksumAlgorithm : unsigned {
bool fromJSON(const llvm::json::Value &, ChecksumAlgorithm &, llvm::json::Path);
llvm::json::Value toJSON(const ChecksumAlgorithm &);
+/// Some predefined types for the CompletionItem. Please note that not all
+/// clients have specific icons for all of them.
+enum CompletionItemType : unsigned {
+ eCompletionItemTypeMethod,
+ eCompletionItemTypeFunction,
+ eCompletionItemTypeConstructor,
+ eCompletionItemTypeField,
+ eCompletionItemTypeVariable,
+ eCompletionItemTypeClass,
+ eCompletionItemTypeInterface,
+ eCompletionItemTypeModule,
+ eCompletionItemTypeProperty,
+ eCompletionItemTypeUnit,
+ eCompletionItemTypeValue,
+ eCompletionItemTypeEnum,
+ eCompletionItemTypeKeyword,
+ eCompletionItemTypeSnippet,
+ eCompletionItemTypeText,
+ eCompletionItemTypeColor,
+ eCompletionItemTypeFile,
+ eCompletionItemTypeReference,
+ eCompletionItemTypeCustomColor,
+};
+bool fromJSON(const llvm::json::Value &, CompletionItemType &,
+ llvm::json::Path);
+llvm::json::Value toJSON(const CompletionItemType &);
+
+/// `CompletionItems` are the suggestions returned from the `completions`
+/// request.
+struct CompletionItem {
+ /// The label of this completion item. By default this is also the text that
+ /// is inserted when selecting this completion.
+ std::string label;
+
+ /// If text is returned and not an empty string, then it is inserted instead
+ /// of the label.
+ std::string text;
+
+ /// A string that should be used when comparing this item with other items. If
+ /// not returned or an empty string, the `label` is used instead.
+ std::string sortText;
+
+ /// A human-readable string with additional information about this item, like
+ /// type or symbol information.
+ std::string detail;
+
+ /// The item's type. Typically the client uses this information to render the
+ /// item in the UI with an icon.
+ std::optional<CompletionItemType> type;
+
+ /// Start position (within the `text` attribute of the `completions`
+ /// request) where the completion text is added. The position is measured in
+ /// UTF-16 code units and the client capability `columnsStartAt1` determines
+ /// whether it is 0- or 1-based. If the start position is omitted the text
+ /// is added at the location specified by the `column` attribute of the
+ /// `completions` request.
+ int64_t start = 0;
+
+ /// Length determines how many characters are overwritten by the completion
+ /// text and it is measured in UTF-16 code units. If missing the value 0 is
+ /// assumed which results in the completion text being inserted.
+ int64_t length = 0;
+
+ /// Determines the start of the new selection after the text has been
+ /// inserted (or replaced). `selectionStart` is measured in UTF-16 code
+ /// units and must be in the range 0 and length of the completion text. If
+ /// omitted the selection starts at the end of the completion text.
+ int64_t selectionStart = 0;
+
+ /// Determines the length of the new selection after the text has been
+ /// inserted (or replaced) and it is measured in UTF-16 code units. The
+ /// selection can not extend beyond the bounds of the completion text. If
+ /// omitted the length is assumed to be 0.
+ int64_t selectionLength = 0;
+};
+bool fromJSON(const llvm::json::Value &, CompletionItem &, llvm::json::Path);
+llvm::json::Value toJSON(const CompletionItem &);
+
/// Describes one or more type of breakpoint a BreakpointMode applies to. This
/// is a non-exhaustive enumeration and may expand as future breakpoint types
/// are added.
@@ -241,8 +320,11 @@ enum AdapterFeature : unsigned {
/// The debug adapter supports the `terminateDebuggee` attribute on the
/// `disconnect` request.
eAdapterFeatureTerminateDebuggee,
+ /// The debug adapter supports the `supportsModuleSymbols` request.
+ /// This request is a custom request of lldb-dap.
+ eAdapterFeatureSupportsModuleSymbolsRequest,
eAdapterFeatureFirst = eAdapterFeatureANSIStyling,
- eAdapterFeatureLast = eAdapterFeatureTerminateDebuggee,
+ eAdapterFeatureLast = eAdapterFeatureSupportsModuleSymbolsRequest,
};
bool fromJSON(const llvm::json::Value &, AdapterFeature &, llvm::json::Path);
llvm::json::Value toJSON(const AdapterFeature &);
@@ -336,7 +418,12 @@ struct Source {
/// skipped on stepping.
std::optional<PresentationHint> presentationHint;
- // unsupported keys: origin, sources, adapterData, checksums
+ /// Additional data that a debug adapter might want to loop through the
+ /// client. The client should leave the data intact and persist it across
+ /// sessions. The client should not interpret the data.
+ std::optional<SourceLLDBData> adapterData;
+
+ // unsupported keys: origin, sources, checksums
};
bool fromJSON(const llvm::json::Value &, Source::PresentationHint &,
llvm::json::Path);
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.cpp b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
index 5fce9fe..843a5eb 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.cpp
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.cpp
@@ -10,7 +10,9 @@
#include "BreakpointBase.h"
#include "DAP.h"
#include "JSONUtils.h"
+#include "ProtocolUtils.h"
#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBFileSpec.h"
#include "lldb/API/SBFileSpecList.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBInstruction.h"
@@ -46,41 +48,20 @@ llvm::Error SourceBreakpoint::SetBreakpoint(const protocol::Source &source) {
if (source.sourceReference) {
// Breakpoint set by assembly source.
- std::optional<lldb::addr_t> raw_addr =
- m_dap.GetSourceReferenceAddress(*source.sourceReference);
- if (!raw_addr)
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Invalid sourceReference.");
-
- lldb::SBAddress source_address(*raw_addr, m_dap.target);
- if (!source_address.IsValid())
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Invalid sourceReference.");
-
- lldb::SBSymbol symbol = source_address.GetSymbol();
- if (!symbol.IsValid()) {
- // FIXME: Support assembly breakpoints without a valid symbol.
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Breakpoints in assembly without a valid "
- "symbol are not supported yet.");
+ if (source.adapterData && source.adapterData->persistenceData) {
+ // Prefer use the adapter persitence data, because this could be a
+ // breakpoint from a previous session where the `sourceReference` is not
+ // valid anymore.
+ if (llvm::Error error = CreateAssemblyBreakpointWithPersistenceData(
+ *source.adapterData->persistenceData))
+ return error;
+ } else {
+ if (llvm::Error error = CreateAssemblyBreakpointWithSourceReference(
+ *source.sourceReference))
+ return error;
}
-
- lldb::SBInstructionList inst_list =
- m_dap.target.ReadInstructions(symbol.GetStartAddress(), m_line);
- if (inst_list.GetSize() < m_line)
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- "Invalid instruction list size.");
-
- lldb::SBAddress address =
- inst_list.GetInstructionAtIndex(m_line - 1).GetAddress();
-
- m_bp = m_dap.target.BreakpointCreateBySBAddress(address);
} else {
- // Breakpoint set by a regular source file.
- const auto source_path = source.path.value_or("");
- lldb::SBFileSpecList module_list;
- m_bp = m_dap.target.BreakpointCreateByLocation(source_path.c_str(), m_line,
- m_column, 0, module_list);
+ CreatePathBreakpoint(source);
}
if (!m_log_message.empty())
@@ -97,6 +78,60 @@ void SourceBreakpoint::UpdateBreakpoint(const SourceBreakpoint &request_bp) {
BreakpointBase::UpdateBreakpoint(request_bp);
}
+void SourceBreakpoint::CreatePathBreakpoint(const protocol::Source &source) {
+ const auto source_path = source.path.value_or("");
+ lldb::SBFileSpecList module_list;
+ m_bp = m_dap.target.BreakpointCreateByLocation(source_path.c_str(), m_line,
+ m_column, 0, module_list);
+}
+
+llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithSourceReference(
+ int64_t source_reference) {
+ std::optional<lldb::addr_t> raw_addr =
+ m_dap.GetSourceReferenceAddress(source_reference);
+ if (!raw_addr)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid sourceReference.");
+
+ lldb::SBAddress source_address(*raw_addr, m_dap.target);
+ if (!source_address.IsValid())
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid sourceReference.");
+
+ lldb::SBSymbol symbol = source_address.GetSymbol();
+ if (!symbol.IsValid()) {
+ // FIXME: Support assembly breakpoints without a valid symbol.
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Breakpoints in assembly without a valid "
+ "symbol are not supported yet.");
+ }
+
+ lldb::SBInstructionList inst_list =
+ m_dap.target.ReadInstructions(symbol.GetStartAddress(), m_line);
+ if (inst_list.GetSize() < m_line)
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid instruction list size.");
+
+ lldb::SBAddress address =
+ inst_list.GetInstructionAtIndex(m_line - 1).GetAddress();
+
+ m_bp = m_dap.target.BreakpointCreateBySBAddress(address);
+ return llvm::Error::success();
+}
+
+llvm::Error SourceBreakpoint::CreateAssemblyBreakpointWithPersistenceData(
+ const protocol::PersistenceData &persistence_data) {
+ lldb::SBFileSpec file_spec(persistence_data.module_path.c_str());
+ lldb::SBFileSpecList comp_unit_list;
+ lldb::SBFileSpecList file_spec_list;
+ file_spec_list.Append(file_spec);
+ m_bp = m_dap.target.BreakpointCreateByName(
+ persistence_data.symbol_name.c_str(), lldb::eFunctionNameTypeFull,
+ lldb::eLanguageTypeUnknown, m_line - 1, true, file_spec_list,
+ comp_unit_list);
+ return llvm::Error::success();
+}
+
lldb::SBError SourceBreakpoint::AppendLogMessagePart(llvm::StringRef part,
bool is_expr) {
if (is_expr) {
diff --git a/lldb/tools/lldb-dap/SourceBreakpoint.h b/lldb/tools/lldb-dap/SourceBreakpoint.h
index 857ac42..34054a8 100644
--- a/lldb/tools/lldb-dap/SourceBreakpoint.h
+++ b/lldb/tools/lldb-dap/SourceBreakpoint.h
@@ -11,6 +11,7 @@
#include "Breakpoint.h"
#include "DAPForward.h"
+#include "Protocol/DAPTypes.h"
#include "Protocol/ProtocolTypes.h"
#include "lldb/API/SBError.h"
#include "llvm/ADT/StringRef.h"
@@ -50,6 +51,12 @@ public:
uint32_t GetColumn() const { return m_column; }
protected:
+ void CreatePathBreakpoint(const protocol::Source &source);
+ llvm::Error
+ CreateAssemblyBreakpointWithSourceReference(int64_t source_reference);
+ llvm::Error CreateAssemblyBreakpointWithPersistenceData(
+ const protocol::PersistenceData &persistence_data);
+
// logMessage part can be either a raw text or an expression.
struct LogMessagePart {
LogMessagePart(llvm::StringRef text, bool is_expr)
diff --git a/lldb/tools/lldb-dap/Transport.cpp b/lldb/tools/lldb-dap/Transport.cpp
index d602920..8f71f88 100644
--- a/lldb/tools/lldb-dap/Transport.cpp
+++ b/lldb/tools/lldb-dap/Transport.cpp
@@ -14,7 +14,8 @@
using namespace llvm;
using namespace lldb;
using namespace lldb_private;
-using namespace lldb_dap;
+
+namespace lldb_dap {
Transport::Transport(llvm::StringRef client_name, lldb_dap::Log *log,
lldb::IOObjectSP input, lldb::IOObjectSP output)
@@ -24,3 +25,5 @@ Transport::Transport(llvm::StringRef client_name, lldb_dap::Log *log,
void Transport::Log(llvm::StringRef message) {
DAP_LOG(m_log, "({0}) {1}", m_client_name, message);
}
+
+} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h
index 51f62e7..4a9dd76 100644
--- a/lldb/tools/lldb-dap/Transport.h
+++ b/lldb/tools/lldb-dap/Transport.h
@@ -15,6 +15,7 @@
#define LLDB_TOOLS_LLDB_DAP_TRANSPORT_H
#include "DAPForward.h"
+#include "Protocol/ProtocolBase.h"
#include "lldb/Host/JSONTransport.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
@@ -23,17 +24,15 @@ namespace lldb_dap {
/// A transport class that performs the Debug Adapter Protocol communication
/// with the client.
-class Transport : public lldb_private::HTTPDelimitedJSONTransport {
+class Transport final
+ : public lldb_private::HTTPDelimitedJSONTransport<
+ protocol::Request, protocol::Response, protocol::Event> {
public:
Transport(llvm::StringRef client_name, lldb_dap::Log *log,
lldb::IOObjectSP input, lldb::IOObjectSP output);
virtual ~Transport() = default;
- virtual void Log(llvm::StringRef message) override;
-
- /// Returns the name of this transport client, for example `stdin/stdout` or
- /// `client_1`.
- llvm::StringRef GetClientName() { return m_client_name; }
+ void Log(llvm::StringRef message) override;
private:
llvm::StringRef m_client_name;
diff --git a/lldb/tools/lldb-dap/package-lock.json b/lldb/tools/lldb-dap/package-lock.json
index 1969b19..26db1ce 100644
--- a/lldb/tools/lldb-dap/package-lock.json
+++ b/lldb/tools/lldb-dap/package-lock.json
@@ -1,20 +1,24 @@
{
"name": "lldb-dap",
- "version": "0.2.15",
+ "version": "0.2.16",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lldb-dap",
- "version": "0.2.15",
+ "version": "0.2.16",
"license": "Apache 2.0 License with LLVM exceptions",
"devDependencies": {
"@types/node": "^18.19.41",
+ "@types/tabulator-tables": "^6.2.10",
"@types/vscode": "1.75.0",
+ "@types/vscode-webview": "^1.57.5",
"@vscode/debugprotocol": "^1.68.0",
"@vscode/vsce": "^3.2.2",
+ "esbuild": "^0.25.9",
"prettier": "^3.4.2",
"prettier-plugin-curly": "^0.3.1",
+ "tabulator-tables": "^6.3.1",
"typescript": "^5.7.3"
},
"engines": {
@@ -318,6 +322,448 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -399,6 +845,13 @@
"undici-types": "~5.26.4"
}
},
+ "node_modules/@types/tabulator-tables": {
+ "version": "6.2.10",
+ "resolved": "https://registry.npmjs.org/@types/tabulator-tables/-/tabulator-tables-6.2.10.tgz",
+ "integrity": "sha512-g6o0gG3lu/ozmxPw9rLY1p57T6rvV8OhbJKyzWwPwjdnN3JuSQ3gWxb06v2+dl2tdoqNXTvlylipSSKpS8UzzQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/vscode": {
"version": "1.75.0",
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.0.tgz",
@@ -406,6 +859,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/vscode-webview": {
+ "version": "1.57.5",
+ "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz",
+ "integrity": "sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@vscode/debugprotocol": {
"version": "1.68.0",
"resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz",
@@ -1169,6 +1629,48 @@
"node": ">= 0.4"
}
},
+ "node_modules/esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
"node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -2557,6 +3059,13 @@
"node": ">=4"
}
},
+ "node_modules/tabulator-tables": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz",
+ "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/tar-fs": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index d677a81..8c6c1b4 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -29,11 +29,15 @@
],
"devDependencies": {
"@types/node": "^18.19.41",
+ "@types/tabulator-tables": "^6.2.10",
"@types/vscode": "1.75.0",
+ "@types/vscode-webview": "^1.57.5",
"@vscode/debugprotocol": "^1.68.0",
"@vscode/vsce": "^3.2.2",
+ "esbuild": "^0.25.9",
"prettier": "^3.4.2",
"prettier-plugin-curly": "^0.3.1",
+ "tabulator-tables": "^6.3.1",
"typescript": "^5.7.3"
},
"activationEvents": [
@@ -42,8 +46,11 @@
],
"main": "./out/extension",
"scripts": {
- "vscode:prepublish": "tsc -p ./",
- "watch": "tsc -watch -p ./",
+ "bundle-symbols-table-view": "npx tsc -p src-ts/webview --noEmit && npx esbuild src-ts/webview/symbols-table-view.ts --bundle --format=iife --outdir=./out/webview",
+ "bundle-tabulator": "cp node_modules/tabulator-tables/dist/js/tabulator.min.js ./out/webview/ && cp node_modules/tabulator-tables/dist/css/tabulator_midnight.min.css ./out/webview/ && cp node_modules/tabulator-tables/dist/css/tabulator_simple.min.css ./out/webview/",
+ "bundle-webview": "npm run bundle-symbols-table-view && npm run bundle-tabulator",
+ "vscode:prepublish": "npm run bundle-webview && tsc -p ./",
+ "watch": "npm run bundle-webview && tsc -watch -p ./",
"format": "npx prettier './src-ts/' --write",
"package": "rm -rf ./out/lldb-dap.vsix && vsce package --out ./out/lldb-dap.vsix",
"publish": "vsce publish",
@@ -259,6 +266,15 @@
{
"command": "lldb-dap.modules.copyProperty",
"title": "Copy Value"
+ },
+ {
+ "command": "lldb-dap.modules.showSymbols",
+ "title": "Show Module Symbols"
+ },
+ {
+ "category": "lldb-dap",
+ "command": "lldb-dap.debug.showSymbols",
+ "title": "Show Symbols of a Module"
}
],
"menus": {
@@ -266,12 +282,24 @@
{
"command": "lldb-dap.modules.copyProperty",
"when": "false"
+ },
+ {
+ "command": "lldb-dap.modules.showSymbols",
+ "when": "false"
+ },
+ {
+ "command": "lldb-dap.debug.showSymbols",
+ "when": "debuggersAvailable && debugType == 'lldb-dap' && lldb-dap.supportsModuleSymbolsRequest"
}
],
"view/item/context": [
{
"command": "lldb-dap.modules.copyProperty",
"when": "view == lldb-dap.modules && viewItem == property"
+ },
+ {
+ "command": "lldb-dap.modules.showSymbols",
+ "when": "view == lldb-dap.modules && viewItem == module && lldb-dap.supportsModuleSymbolsRequest"
}
]
},
@@ -370,6 +398,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 6e94400..f7e92ee 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 = {
@@ -217,7 +300,15 @@ export class LLDBDapDescriptorFactory
constructor(
private readonly logger: vscode.LogOutputChannel,
private logFilePath: LogFilePathProvider,
- ) {}
+ ) {
+ vscode.commands.registerCommand(
+ "lldb-dap.createDebugAdapterDescriptor",
+ (
+ session: vscode.DebugSession,
+ executable: vscode.DebugAdapterExecutable | undefined,
+ ) => this.createDebugAdapterDescriptor(session, executable),
+ );
+ }
async createDebugAdapterDescriptor(
session: vscode.DebugSession,
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 8c04ec2..1ae8711 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-configuration-provider.ts
@@ -69,6 +69,10 @@ const configurations: Record<string, DefaultConfig> = {
terminateCommands: { type: "stringArray", default: [] },
};
+export function getDefaultConfigKey(key: string): string | number | boolean | string[] | undefined {
+ return configurations[key]?.default;
+}
+
export class LLDBDapConfigurationProvider
implements vscode.DebugConfigurationProvider
{
@@ -76,7 +80,29 @@ export class LLDBDapConfigurationProvider
private readonly server: LLDBDapServer,
private readonly logger: vscode.LogOutputChannel,
private readonly logFilePath: LogFilePathProvider,
- ) {}
+ ) {
+ vscode.commands.registerCommand(
+ "lldb-dap.resolveDebugConfiguration",
+ (
+ folder: vscode.WorkspaceFolder | undefined,
+ debugConfiguration: vscode.DebugConfiguration,
+ token?: vscode.CancellationToken,
+ ) => this.resolveDebugConfiguration(folder, debugConfiguration, token),
+ );
+ vscode.commands.registerCommand(
+ "lldb-dap.resolveDebugConfigurationWithSubstitutedVariables",
+ (
+ folder: vscode.WorkspaceFolder | undefined,
+ debugConfiguration: vscode.DebugConfiguration,
+ token?: vscode.CancellationToken,
+ ) =>
+ this.resolveDebugConfigurationWithSubstitutedVariables(
+ folder,
+ debugConfiguration,
+ token,
+ ),
+ );
+ }
async resolveDebugConfiguration(
folder: vscode.WorkspaceFolder | undefined,
diff --git a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
index 7d7f73d..6e89d44 100644
--- a/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
+++ b/lldb/tools/lldb-dap/src-ts/debug-session-tracker.ts
@@ -1,11 +1,17 @@
import { DebugProtocol } from "@vscode/debugprotocol";
import * as vscode from "vscode";
+export interface LLDBDapCapabilities extends DebugProtocol.Capabilities {
+ /** The debug adapter supports the `moduleSymbols` request. */
+ supportsModuleSymbolsRequest?: boolean;
+}
+
/** A helper type for mapping event types to their corresponding data type. */
// prettier-ignore
interface EventMap {
"module": DebugProtocol.ModuleEvent;
"exited": DebugProtocol.ExitedEvent;
+ "capabilities": DebugProtocol.CapabilitiesEvent;
}
/** A type assertion to check if a ProtocolMessage is an event or if it is a specific event. */
@@ -39,6 +45,9 @@ export class DebugSessionTracker
private modulesChanged = new vscode.EventEmitter<
vscode.DebugSession | undefined
>();
+ private sessionReceivedCapabilities =
+ new vscode.EventEmitter<[ vscode.DebugSession, LLDBDapCapabilities ]>();
+ private sessionExited = new vscode.EventEmitter<vscode.DebugSession>();
/**
* Fired when modules are changed for any active debug session.
@@ -48,6 +57,15 @@ export class DebugSessionTracker
onDidChangeModules: vscode.Event<vscode.DebugSession | undefined> =
this.modulesChanged.event;
+ /** Fired when a debug session is initialized. */
+ onDidReceiveSessionCapabilities:
+ vscode.Event<[ vscode.DebugSession, LLDBDapCapabilities ]> =
+ this.sessionReceivedCapabilities.event;
+
+ /** Fired when a debug session is exiting. */
+ onDidExitSession: vscode.Event<vscode.DebugSession> =
+ this.sessionExited.event;
+
constructor(private logger: vscode.LogOutputChannel) {
this.onDidChangeModules(this.moduleChangedListener, this);
vscode.debug.onDidChangeActiveDebugSession((session) =>
@@ -146,6 +164,10 @@ export class DebugSessionTracker
this.logger.info(
`Session "${session.name}" exited with code ${exitCode}`,
);
+
+ this.sessionExited.fire(session);
+ } else if (isEvent(message, "capabilities")) {
+ this.sessionReceivedCapabilities.fire([ session, message.body.capabilities ]);
}
}
}
diff --git a/lldb/tools/lldb-dap/src-ts/extension.ts b/lldb/tools/lldb-dap/src-ts/extension.ts
index 4b7a35e..7119cba 100644
--- a/lldb/tools/lldb-dap/src-ts/extension.ts
+++ b/lldb/tools/lldb-dap/src-ts/extension.ts
@@ -12,6 +12,7 @@ import {
ModuleProperty,
} from "./ui/modules-data-provider";
import { LogFilePathProvider } from "./logging";
+import { SymbolsProvider } from "./ui/symbols-provider";
/**
* This class represents the extension and manages its life cycle. Other extensions
@@ -19,6 +20,7 @@ import { LogFilePathProvider } from "./logging";
*/
export class LLDBDapExtension extends DisposableContext {
constructor(
+ context: vscode.ExtensionContext,
logger: vscode.LogOutputChannel,
logFilePath: LogFilePathProvider,
outputChannel: vscode.OutputChannel,
@@ -52,10 +54,12 @@ export class LLDBDapExtension extends DisposableContext {
vscode.window.registerUriHandler(new LaunchUriHandler()),
);
- vscode.commands.registerCommand(
+ this.pushSubscription(vscode.commands.registerCommand(
"lldb-dap.modules.copyProperty",
(node: ModuleProperty) => vscode.env.clipboard.writeText(node.value),
- );
+ ));
+
+ this.pushSubscription(new SymbolsProvider(sessionTracker, context));
}
}
@@ -67,7 +71,7 @@ export async function activate(context: vscode.ExtensionContext) {
outputChannel.info("LLDB-DAP extension activating...");
const logFilePath = new LogFilePathProvider(context, outputChannel);
context.subscriptions.push(
- new LLDBDapExtension(outputChannel, logFilePath, outputChannel),
+ new LLDBDapExtension(context, outputChannel, logFilePath, outputChannel),
);
outputChannel.info("LLDB-DAP extension activated");
}
diff --git a/lldb/tools/lldb-dap/src-ts/index.d.ts b/lldb/tools/lldb-dap/src-ts/index.d.ts
new file mode 100644
index 0000000..d4618f4
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/index.d.ts
@@ -0,0 +1,14 @@
+export {};
+
+/// The symbol type we get from the lldb-dap server
+export declare interface SymbolType {
+ id: number;
+ isDebug: boolean;
+ isSynthetic: boolean;
+ isExternal: boolean;
+ type: string;
+ fileAddress: number;
+ loadAddress?: number;
+ size: number;
+ name: string;
+}
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 03a0fc9..300b12d 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,14 @@ 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(
+ "lldb-dap.getServerProcess",
+ () => this.serverProcess,
+ );
+ }
/**
* Starts the server with the provided options. The server will be restarted or reused as
@@ -27,7 +35,7 @@ export class LLDBDapServer implements vscode.Disposable {
options?: child_process.SpawnOptionsWithoutStdio,
): Promise<{ host: string; port: number } | undefined> {
const dapArgs = [...args, "--connection", "listen://localhost:0"];
- if (!(await this.shouldContinueStartup(dapPath, dapArgs))) {
+ if (!(await this.shouldContinueStartup(dapPath, dapArgs, options?.env))) {
return undefined;
}
@@ -39,8 +47,7 @@ export class LLDBDapServer implements vscode.Disposable {
const process = child_process.spawn(dapPath, dapArgs, options);
process.on("error", (error) => {
reject(error);
- this.serverProcess = undefined;
- this.serverInfo = undefined;
+ this.cleanUp(process);
});
process.on("exit", (code, signal) => {
let errorMessage = "Server process exited early";
@@ -50,8 +57,7 @@ export class LLDBDapServer implements vscode.Disposable {
errorMessage += ` due to signal ${signal}`;
}
reject(new Error(errorMessage));
- this.serverProcess = undefined;
- this.serverInfo = undefined;
+ this.cleanUp(process);
});
process.stdout.setEncoding("utf8").on("data", (data) => {
const connection = /connection:\/\/\[([^\]]+)\]:(\d+)/.exec(
@@ -65,6 +71,7 @@ export class LLDBDapServer implements vscode.Disposable {
}
});
this.serverProcess = process;
+ this.serverSpawnInfo = this.getSpawnInfo(dapPath, dapArgs, options?.env);
});
return this.serverInfo;
}
@@ -80,12 +87,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;
}
@@ -97,11 +106,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.`,
},
@@ -126,7 +135,30 @@ Restarting the server will interrupt any existing debug sessions and start a new
return;
}
this.serverProcess.kill();
- this.serverProcess = undefined;
- this.serverInfo = undefined;
+ this.cleanUp(this.serverProcess);
+ }
+
+ cleanUp(process: child_process.ChildProcessWithoutNullStreams) {
+ // If the following don't equal, then the fields have already been updated
+ // (either a new process has started, or the fields were already cleaned
+ // up), and so the cleanup should be skipped.
+ if (this.serverProcess === process) {
+ this.serverProcess = undefined;
+ 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/modules-data-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
index d0fb927..96343cb 100644
--- a/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
+++ b/lldb/tools/lldb-dap/src-ts/ui/modules-data-provider.ts
@@ -19,6 +19,7 @@ class ModuleItem extends vscode.TreeItem {
constructor(module: DebugProtocol.Module) {
super(module.name, vscode.TreeItemCollapsibleState.Collapsed);
this.description = module.symbolStatus;
+ this.contextValue = "module";
}
static getProperties(module: DebugProtocol.Module): ModuleProperty[] {
diff --git a/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts b/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts
new file mode 100644
index 0000000..951a597
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/ui/symbols-provider.ts
@@ -0,0 +1,127 @@
+import * as vscode from "vscode";
+import { DebugProtocol } from "@vscode/debugprotocol";
+
+import { DebugSessionTracker } from "../debug-session-tracker";
+import { DisposableContext } from "../disposable-context";
+
+import { SymbolType } from "..";
+import { getSymbolsTableHTMLContent } from "./symbols-webview-html";
+import { getDefaultConfigKey } from "../debug-configuration-provider";
+
+export class SymbolsProvider extends DisposableContext {
+ constructor(
+ private readonly tracker: DebugSessionTracker,
+ private readonly extensionContext: vscode.ExtensionContext,
+ ) {
+ super();
+
+ this.pushSubscription(vscode.commands.registerCommand(
+ "lldb-dap.debug.showSymbols",
+ () => {
+ const session = vscode.debug.activeDebugSession;
+ if (!session) return;
+
+ this.SelectModuleAndShowSymbols(session);
+ },
+ ));
+
+ this.pushSubscription(vscode.commands.registerCommand(
+ "lldb-dap.modules.showSymbols",
+ (moduleItem: DebugProtocol.Module) => {
+ const session = vscode.debug.activeDebugSession;
+ if (!session) return;
+
+ this.showSymbolsForModule(session, moduleItem);
+ },
+ ));
+
+ this.tracker.onDidReceiveSessionCapabilities(([ _session, capabilities ]) => {
+ if (capabilities.supportsModuleSymbolsRequest) {
+ vscode.commands.executeCommand(
+ "setContext", "lldb-dap.supportsModuleSymbolsRequest", true);
+ }
+ });
+
+ this.tracker.onDidExitSession((_session) => {
+ vscode.commands.executeCommand("setContext", "lldb-dap.supportsModuleSymbolsRequest", false);
+ });
+ }
+
+ private async SelectModuleAndShowSymbols(session: vscode.DebugSession) {
+ const modules = this.tracker.debugSessionModules(session);
+ if (!modules || modules.length === 0) {
+ return;
+ }
+
+ // Let the user select a module to show symbols for
+ const selectedModule = await vscode.window.showQuickPick(modules.map(m => new ModuleQuickPickItem(m)), {
+ placeHolder: "Select a module to show symbols for"
+ });
+ if (!selectedModule) {
+ return;
+ }
+
+ 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());
+ await this.showSymbolsInNewTab(module.name.toString(), symbols);
+ } catch (error) {
+ if (error instanceof Error) {
+ await vscode.window.showErrorMessage("Failed to retrieve symbols: " + error.message);
+ } else {
+ await vscode.window.showErrorMessage("Failed to retrieve symbols due to an unknown error.");
+ }
+
+ return;
+ }
+ }
+
+ private async getSymbolsForModule(session: vscode.DebugSession, moduleId: string): Promise<SymbolType[]> {
+ const symbols_response: { symbols: Array<SymbolType> } = await session.customRequest("__lldb_moduleSymbols", { moduleId, moduleName: '' });
+ return symbols_response?.symbols || [];
+ }
+
+ private async showSymbolsInNewTab(moduleName: string, symbols: SymbolType[]) {
+ const panel = vscode.window.createWebviewPanel(
+ "lldb-dap.symbols",
+ `Symbols for ${moduleName}`,
+ vscode.ViewColumn.Active,
+ {
+ enableScripts: true,
+ localResourceRoots: [
+ this.getExtensionResourcePath()
+ ]
+ }
+ );
+
+ let tabulatorJsFilename = "tabulator_simple.min.css";
+ if (vscode.window.activeColorTheme.kind === vscode.ColorThemeKind.Dark || vscode.window.activeColorTheme.kind === vscode.ColorThemeKind.HighContrast) {
+ tabulatorJsFilename = "tabulator_midnight.min.css";
+ }
+ const tabulatorCssPath = panel.webview.asWebviewUri(vscode.Uri.joinPath(this.getExtensionResourcePath(), tabulatorJsFilename));
+ const tabulatorJsPath = panel.webview.asWebviewUri(vscode.Uri.joinPath(this.getExtensionResourcePath(), "tabulator.min.js"));
+ const symbolsTableScriptPath = panel.webview.asWebviewUri(vscode.Uri.joinPath(this.getExtensionResourcePath(), "symbols-table-view.js"));
+
+ panel.webview.html = getSymbolsTableHTMLContent(tabulatorJsPath, tabulatorCssPath, symbolsTableScriptPath);
+ await panel.webview.postMessage({ command: "updateSymbols", symbols: symbols });
+ }
+
+ private getExtensionResourcePath(): vscode.Uri {
+ return vscode.Uri.joinPath(this.extensionContext.extensionUri, "out", "webview");
+ }
+}
+
+class ModuleQuickPickItem implements vscode.QuickPickItem {
+ constructor(public readonly module: DebugProtocol.Module) {}
+
+ get label(): string {
+ return this.module.name;
+ }
+
+ get description(): string {
+ return this.module.id.toString();
+ }
+}
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
new file mode 100644
index 0000000..c00e0d4
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/ui/symbols-webview-html.ts
@@ -0,0 +1,67 @@
+import * as vscode from "vscode";
+
+export function getSymbolsTableHTMLContent(tabulatorJsPath: vscode.Uri, tabulatorCssPath: vscode.Uri, symbolsTableScriptPath: vscode.Uri): string {
+ return `<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <link href="${tabulatorCssPath}" rel="stylesheet">
+ <style>
+ .tabulator {
+ background-color: var(--vscode-editor-background);
+ 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-tree-tableOddRowsBackground);
+ color: var(--vscode-editor-foreground);
+ }
+
+ .tabulator-row {
+ background-color: var(--vscode-editor-background);
+ color: var(--vscode-editor-foreground);
+ }
+
+ .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 .tabulator-tableholder .tabulator-table {
+ background-color: var(--vscode-editor-background);
+ color: var(--vscode-editor-foreground);
+ }
+
+ .tabulator-cell {
+ text-overflow: clip !important;
+ }
+
+ #symbols-table {
+ width: 100%;
+ height: 100vh;
+ }
+ </style>
+</head>
+<body>
+ <div id="symbols-table"></div>
+ <script src="${tabulatorJsPath}"></script>
+ <script src="${symbolsTableScriptPath}"></script>
+</body>
+</html>`;
+} \ No newline at end of file
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
new file mode 100644
index 0000000..9d34681
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/webview/symbols-table-view.ts
@@ -0,0 +1,115 @@
+import type { CellComponent, ColumnDefinition } from "tabulator-tables";
+import type { SymbolType } from ".."
+
+/// SVG from https://github.com/olifolkerd/tabulator/blob/master/src/js/modules/Format/defaults/formatters/tickCross.js
+/// but with the default font color.
+/// hopefully in the future we can set the color as parameter: https://github.com/olifolkerd/tabulator/pull/4791
+const TICK_ELEMENT = `<svg enable-background="new 0 0 24 24" height="14" width="14" viewBox="0 0 24 24" xml:space="preserve" ><path fill="var(--vscode-editor-foreground)" clip-rule="evenodd" d="M21.652,3.211c-0.293-0.295-0.77-0.295-1.061,0L9.41,14.34 c-0.293,0.297-0.771,0.297-1.062,0L3.449,9.351C3.304,9.203,3.114,9.13,2.923,9.129C2.73,9.128,2.534,9.201,2.387,9.351 l-2.165,1.946C0.078,11.445,0,11.63,0,11.823c0,0.194,0.078,0.397,0.223,0.544l4.94,5.184c0.292,0.296,0.771,0.776,1.062,1.07 l2.124,2.141c0.292,0.293,0.769,0.293,1.062,0l14.366-14.34c0.293-0.294,0.293-0.777,0-1.071L21.652,3.211z" fill-rule="evenodd"/></svg>`;
+
+function getTabulatorHexaFormatter(padding: number): (cell: CellComponent) => string {
+ return (cell: CellComponent) => {
+ const val = cell.getValue();
+ if (val === undefined || val === null) {
+ return "";
+ }
+
+ return val !== undefined ? "0x" + val.toString(16).toLowerCase().padStart(padding, "0") : "";
+ };
+}
+
+const SYMBOL_TABLE_COLUMNS: ColumnDefinition[] = [
+ { title: "ID", field: "id", headerTooltip: true, sorter: "number", widthGrow: 0.6 },
+ {
+ title: "Name",
+ field: "name",
+ headerTooltip: true,
+ sorter: "string",
+ widthGrow: 2.5,
+ minWidth: 200,
+ tooltip : (_event: MouseEvent, cell: CellComponent) => {
+ const rowData = cell.getRow().getData();
+ return rowData.name;
+ }
+ },
+ {
+ title: "Debug",
+ field: "isDebug",
+ headerTooltip: true,
+ hozAlign: "center",
+ widthGrow: 0.8,
+ formatter: "tickCross",
+ formatterParams: {
+ tickElement: TICK_ELEMENT,
+ crossElement: false,
+ }
+ },
+ {
+ title: "Synthetic",
+ field: "isSynthetic",
+ headerTooltip: true,
+ hozAlign: "center",
+ widthGrow: 0.8,
+ formatter: "tickCross",
+ formatterParams: {
+ tickElement: TICK_ELEMENT,
+ crossElement: false,
+ }
+ },
+ {
+ title: "External",
+ field: "isExternal",
+ headerTooltip: true,
+ hozAlign: "center",
+ widthGrow: 0.8,
+ formatter: "tickCross",
+ formatterParams: {
+ tickElement: TICK_ELEMENT,
+ crossElement: false,
+ }
+ },
+ { title: "Type", field: "type", sorter: "string" },
+ {
+ title: "File Address",
+ field: "fileAddress",
+ headerTooltip: true,
+ sorter: "number",
+ widthGrow : 1.25,
+ formatter: getTabulatorHexaFormatter(16),
+ },
+ {
+ title: "Load Address",
+ field: "loadAddress",
+ headerTooltip: true,
+ sorter: "number",
+ widthGrow : 1.25,
+ formatter: getTabulatorHexaFormatter(16),
+ },
+ { title: "Size", field: "size", headerTooltip: true, sorter: "number", formatter: getTabulatorHexaFormatter(8) },
+];
+
+const vscode = acquireVsCodeApi();
+const previousState: any = vscode.getState();
+
+declare const Tabulator: any; // HACK: real definition comes from tabulator.min.js
+const SYMBOLS_TABLE = new Tabulator("#symbols-table", {
+ height: "100vh",
+ columns: SYMBOL_TABLE_COLUMNS,
+ layout: "fitColumns",
+ selectableRows: false,
+ data: previousState?.symbols || [],
+});
+
+function updateSymbolsTable(symbols: SymbolType[]) {
+ SYMBOLS_TABLE.setData(symbols);
+}
+
+window.addEventListener("message", (event: MessageEvent<any>) => {
+ const message = event.data;
+ switch (message.command) {
+ case "updateSymbols":
+ vscode.setState({ symbols: message.symbols });
+ updateSymbolsTable(message.symbols);
+ break;
+ }
+});
+
diff --git a/lldb/tools/lldb-dap/src-ts/webview/tsconfig.json b/lldb/tools/lldb-dap/src-ts/webview/tsconfig.json
new file mode 100644
index 0000000..cfe64fc
--- /dev/null
+++ b/lldb/tools/lldb-dap/src-ts/webview/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "moduleResolution": "node",
+ "module": "esnext",
+ "outDir": "out",
+ "rootDir": ".",
+ "sourceMap": true,
+ "strict": true,
+ "noEmit": true,
+ "target": "es2017"
+ },
+ "include": [
+ "./"
+ ],
+}
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index 8bba416..b74085f 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -39,6 +39,7 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <condition_variable>
#include <cstdio>
@@ -284,7 +285,7 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
});
std::condition_variable dap_sessions_condition;
std::mutex dap_sessions_mutex;
- std::map<IOObject *, DAP *> dap_sessions;
+ std::map<MainLoop *, DAP *> dap_sessions;
unsigned int clientCount = 0;
auto handle = listener->Accept(g_loop, [=, &dap_sessions_condition,
&dap_sessions_mutex, &dap_sessions,
@@ -300,8 +301,10 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
std::thread client([=, &dap_sessions_condition, &dap_sessions_mutex,
&dap_sessions]() {
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, transport);
+ DAP dap(log, default_repl_mode, pre_init_commands, client_name, transport,
+ loop);
if (auto Err = dap.ConfigureIO()) {
llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(),
@@ -311,7 +314,7 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
{
std::scoped_lock<std::mutex> lock(dap_sessions_mutex);
- dap_sessions[io.get()] = &dap;
+ dap_sessions[&loop] = &dap;
}
if (auto Err = dap.Loop()) {
@@ -322,7 +325,7 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
DAP_LOG(log, "({0}) client disconnected", client_name);
std::unique_lock<std::mutex> lock(dap_sessions_mutex);
- dap_sessions.erase(io.get());
+ dap_sessions.erase(&loop);
std::notify_all_at_thread_exit(dap_sessions_condition, std::move(lock));
});
client.detach();
@@ -344,13 +347,14 @@ serveConnection(const Socket::SocketProtocol &protocol, const std::string &name,
bool client_failed = false;
{
std::scoped_lock<std::mutex> lock(dap_sessions_mutex);
- for (auto [sock, dap] : dap_sessions) {
+ for (auto [loop, dap] : dap_sessions) {
if (llvm::Error error = dap->Disconnect()) {
client_failed = true;
- llvm::errs() << "DAP client " << dap->transport.GetClientName()
- << " disconnected failed: "
- << llvm::toString(std::move(error)) << "\n";
+ llvm::WithColor::error() << "DAP client disconnected failed: "
+ << llvm::toString(std::move(error)) << "\n";
}
+ loop->AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
}
}
@@ -550,8 +554,10 @@ int main(int argc, char *argv[]) {
stdout_fd, File::eOpenOptionWriteOnly, NativeFile::Unowned);
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, transport);
+ DAP dap(log.get(), default_repl_mode, pre_init_commands, 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-dap/tsconfig.json b/lldb/tools/lldb-dap/tsconfig.json
index 2092148..06a484a 100644
--- a/lldb/tools/lldb-dap/tsconfig.json
+++ b/lldb/tools/lldb-dap/tsconfig.json
@@ -1,5 +1,6 @@
{
"compilerOptions": {
+ "moduleResolution": "node",
"module": "commonjs",
"outDir": "out",
"rootDir": "src-ts",
@@ -12,5 +13,6 @@
],
"exclude": [
"node_modules",
+ "src-ts/webview",
]
}
diff --git a/lldb/tools/lldb-mcp/CMakeLists.txt b/lldb/tools/lldb-mcp/CMakeLists.txt
new file mode 100644
index 0000000..7fe3301
--- /dev/null
+++ b/lldb/tools/lldb-mcp/CMakeLists.txt
@@ -0,0 +1,33 @@
+add_lldb_tool(lldb-mcp
+ lldb-mcp.cpp
+
+ LINK_COMPONENTS
+ Option
+ Support
+ LINK_LIBS
+ liblldb
+ 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 0000000..4dc3ddd
--- /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 0000000..1f82af9
--- /dev/null
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Protocol/MCP/Protocol.h"
+#include "lldb/Protocol/MCP/Server.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
+
+#if defined(_WIN32)
+#include <fcntl.h>
+#endif
+
+using namespace lldb_protocol::mcp;
+
+using lldb_private::File;
+using lldb_private::MainLoop;
+using lldb_private::MainLoopBase;
+using lldb_private::NativeFile;
+
+static constexpr llvm::StringLiteral kName = "lldb-mcp";
+static constexpr llvm::StringLiteral kVersion = "0.1.0";
+
+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
+
+ lldb::IOObjectSP input = std::make_shared<NativeFile>(
+ fileno(stdin), File::eOpenOptionReadOnly, NativeFile::Unowned);
+
+ lldb::IOObjectSP output = std::make_shared<NativeFile>(
+ fileno(stdout), File::eOpenOptionWriteOnly, NativeFile::Unowned);
+
+ constexpr llvm::StringLiteral client_name = "stdio";
+ static MainLoop loop;
+
+ llvm::sys::SetInterruptFunction([]() {
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ });
+
+ auto transport_up = std::make_unique<lldb_protocol::mcp::MCPTransport>(
+ input, output, std::string(client_name),
+ [&](llvm::StringRef message) { llvm::errs() << message << '\n'; });
+
+ auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
+ std::string(kName), std::string(kVersion), std::move(transport_up), loop);
+
+ if (llvm::Error error = instance_up->Run()) {
+ llvm::logAllUnhandledErrors(std::move(error), llvm::WithColor::error(),
+ "MCP error: ");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
index 9b48796..47b3093 100644
--- a/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
+++ b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp
@@ -121,11 +121,10 @@ private:
/// - Certain inconvenient classes
/// - Records without definitions (forward declarations)
bool ShouldSkipRecord(CXXRecordDecl *Decl) {
- const Type *DeclType = Decl->getTypeForDecl();
- QualType CanonicalType = DeclType->getCanonicalTypeInternal();
return !Manager.isInMainFile(Decl->getBeginLoc()) ||
!Decl->hasDefinition() || Decl->getDefinition() != Decl ||
- lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType);
+ lldb_rpc_gen::TypeIsDisallowedClass(
+ Context.getCanonicalTagType(Decl));
}
/// Check the support level for a type
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index 5533c73..4c5267a 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/CMakeLists.txt b/lldb/unittests/Core/CMakeLists.txt
index a6820bd..6e609a6 100644
--- a/lldb/unittests/Core/CMakeLists.txt
+++ b/lldb/unittests/Core/CMakeLists.txt
@@ -15,6 +15,7 @@ add_lldb_unittest(LLDBCoreTests
SourceManagerTest.cpp
TelemetryTest.cpp
UniqueCStringMapTest.cpp
+ Value.cpp
LINK_COMPONENTS
Support
diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp
index a6e6e75f..cbc0c5d 100644
--- a/lldb/unittests/Core/MangledTest.cpp
+++ b/lldb/unittests/Core/MangledTest.cpp
@@ -889,10 +889,10 @@ TEST_P(DemanglingInfoCorrectnessTestFixutre, Correctness) {
EXPECT_THAT_EXPECTED(qualifiers, llvm::Succeeded());
reconstructed_name += *qualifiers;
- // TODO: should retrieve suffix using the plugin too.
- auto suffix = tracked_name.slice(OB->NameInfo.QualifiersRange.second,
- llvm::StringRef::npos);
- reconstructed_name += suffix;
+ auto suffix =
+ CPlusPlusLanguage::GetDemangledFunctionSuffix(tracked_name, OB->NameInfo);
+ EXPECT_THAT_EXPECTED(suffix, llvm::Succeeded());
+ reconstructed_name += *suffix;
EXPECT_EQ(reconstructed_name, demangled);
}
diff --git a/lldb/unittests/Core/Value.cpp b/lldb/unittests/Core/Value.cpp
new file mode 100644
index 0000000..d18a700
--- /dev/null
+++ b/lldb/unittests/Core/Value.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Core/Value.h"
+#include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
+#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/Symbol/ClangTestUtils.h"
+
+#include "lldb/Utility/DataExtractor.h"
+
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb_private::clang_utils;
+
+TEST(ValueTest, GetValueAsData) {
+ SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX> subsystems;
+ auto holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
+ auto *clang = holder->GetAST();
+
+ Value v(Scalar(42));
+ DataExtractor extractor;
+
+ // no compiler type
+ Status status = v.GetValueAsData(nullptr, extractor, nullptr);
+ ASSERT_TRUE(status.Fail());
+
+ // with compiler type
+ v.SetCompilerType(clang->GetBasicType(lldb::BasicType::eBasicTypeChar));
+
+ status = v.GetValueAsData(nullptr, extractor, nullptr);
+ ASSERT_TRUE(status.Success());
+}
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index 156cd62..716159b 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -1,6 +1,7 @@
add_lldb_unittest(DAPTests
DAPErrorTest.cpp
DAPTest.cpp
+ DAPTypesTest.cpp
FifoFilesTest.cpp
Handler/DisconnectTest.cpp
Handler/ContinueTest.cpp
diff --git a/lldb/unittests/DAP/DAPTest.cpp b/lldb/unittests/DAP/DAPTest.cpp
index 40ffaf8..d5a9591 100644
--- a/lldb/unittests/DAP/DAPTest.cpp
+++ b/lldb/unittests/DAP/DAPTest.cpp
@@ -9,11 +9,9 @@
#include "DAP.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
-#include "Transport.h"
#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include <chrono>
-#include <memory>
#include <optional>
using namespace llvm;
@@ -21,6 +19,7 @@ using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;
+using namespace testing;
class DAPTest : public TransportBase {};
@@ -29,11 +28,13 @@ TEST_F(DAPTest, SendProtocolMessages) {
/*log=*/nullptr,
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/{},
- /*transport=*/*to_dap,
+ /*client_name=*/"test_client",
+ /*transport=*/*transport,
+ /*loop=*/loop,
};
dap.Send(Event{/*event=*/"my-event", /*body=*/std::nullopt});
- ASSERT_THAT_EXPECTED(
- from_dap->Read<protocol::Message>(std::chrono::milliseconds(1)),
- HasValue(testing::VariantWith<Event>(testing::FieldsAre(
- /*event=*/"my-event", /*body=*/std::nullopt))));
+ loop.AddPendingCallback(
+ [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+ EXPECT_CALL(client, Received(IsEvent("my-event", std::nullopt)));
+ ASSERT_THAT_ERROR(dap.Loop(), llvm::Succeeded());
}
diff --git a/lldb/unittests/DAP/DAPTypesTest.cpp b/lldb/unittests/DAP/DAPTypesTest.cpp
new file mode 100644
index 0000000..f398c54
--- /dev/null
+++ b/lldb/unittests/DAP/DAPTypesTest.cpp
@@ -0,0 +1,60 @@
+//===-- DAPTypesTest.cpp ----------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "Protocol/DAPTypes.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+#include <optional>
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_dap;
+using namespace lldb_dap::protocol;
+using lldb_private::roundtripJSON;
+
+TEST(DAPTypesTest, SourceLLDBData) {
+ SourceLLDBData source_data;
+ source_data.persistenceData =
+ PersistenceData{"module_path123", "symbol_name456"};
+
+ llvm::Expected<SourceLLDBData> deserialized_data = roundtripJSON(source_data);
+ ASSERT_THAT_EXPECTED(deserialized_data, llvm::Succeeded());
+
+ EXPECT_EQ(source_data.persistenceData->module_path,
+ deserialized_data->persistenceData->module_path);
+ EXPECT_EQ(source_data.persistenceData->symbol_name,
+ deserialized_data->persistenceData->symbol_name);
+}
+
+TEST(DAPTypesTest, DAPSymbol) {
+ Symbol symbol;
+ symbol.id = 42;
+ symbol.isDebug = true;
+ symbol.isExternal = false;
+ symbol.isSynthetic = true;
+ symbol.type = lldb::eSymbolTypeTrampoline;
+ symbol.fileAddress = 0x12345678;
+ symbol.loadAddress = 0x87654321;
+ symbol.size = 64;
+ symbol.name = "testSymbol";
+
+ llvm::Expected<Symbol> deserialized_symbol = roundtripJSON(symbol);
+ ASSERT_THAT_EXPECTED(deserialized_symbol, llvm::Succeeded());
+
+ EXPECT_EQ(symbol.id, deserialized_symbol->id);
+ EXPECT_EQ(symbol.isDebug, deserialized_symbol->isDebug);
+ EXPECT_EQ(symbol.isExternal, deserialized_symbol->isExternal);
+ EXPECT_EQ(symbol.isSynthetic, deserialized_symbol->isSynthetic);
+ EXPECT_EQ(symbol.type, deserialized_symbol->type);
+ EXPECT_EQ(symbol.fileAddress, deserialized_symbol->fileAddress);
+ EXPECT_EQ(symbol.loadAddress, deserialized_symbol->loadAddress);
+ EXPECT_EQ(symbol.size, deserialized_symbol->size);
+ EXPECT_EQ(symbol.name, deserialized_symbol->name);
+}
diff --git a/lldb/unittests/DAP/Handler/DisconnectTest.cpp b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
index 0546aeb..c6ff1f9 100644
--- a/lldb/unittests/DAP/Handler/DisconnectTest.cpp
+++ b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
@@ -23,18 +23,15 @@ using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;
+using testing::_;
class DisconnectRequestHandlerTest : public DAPTestBase {};
TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminated) {
DisconnectRequestHandler handler(*dap);
- EXPECT_FALSE(dap->disconnecting);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
- EXPECT_TRUE(dap->disconnecting);
- std::vector<Message> messages = DrainOutput();
- EXPECT_THAT(messages,
- testing::Contains(testing::VariantWith<Event>(testing::FieldsAre(
- /*event=*/"terminated", /*body=*/testing::_))));
+ EXPECT_CALL(client, Received(IsEvent("terminated", _)));
+ RunOnce();
}
TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminateCommands) {
@@ -47,17 +44,14 @@ TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminateCommands) {
DisconnectRequestHandler handler(*dap);
- EXPECT_FALSE(dap->disconnecting);
dap->configuration.terminateCommands = {"?script print(1)",
"script print(2)"};
EXPECT_EQ(dap->target.GetProcess().GetState(), lldb::eStateStopped);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
- EXPECT_TRUE(dap->disconnecting);
- std::vector<Message> messages = DrainOutput();
- EXPECT_THAT(messages, testing::ElementsAre(
- OutputMatcher("Running terminateCommands:\n"),
- OutputMatcher("(lldb) script print(2)\n"),
- OutputMatcher("2\n"),
- testing::VariantWith<Event>(testing::FieldsAre(
- /*event=*/"terminated", /*body=*/testing::_))));
+ EXPECT_CALL(client, Received(Output("1\n")));
+ EXPECT_CALL(client, Received(Output("2\n"))).Times(2);
+ EXPECT_CALL(client, Received(Output("(lldb) script print(2)\n")));
+ EXPECT_CALL(client, Received(Output("Running terminateCommands:\n")));
+ EXPECT_CALL(client, Received(IsEvent("terminated", _)));
+ RunOnce();
}
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 3a1b940..c5d47fc 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -108,8 +108,9 @@ TEST(ProtocolTypesTest, Breakpoint) {
breakpoint.id = 42;
breakpoint.verified = true;
breakpoint.message = "Breakpoint set successfully";
- breakpoint.source = Source{"test.cpp", "/path/to/test.cpp", 123,
- Source::eSourcePresentationHintNormal};
+ breakpoint.source =
+ Source{"test.cpp", "/path/to/test.cpp", 123,
+ Source::eSourcePresentationHintNormal, std::nullopt};
breakpoint.line = 10;
breakpoint.column = 5;
breakpoint.endLine = 15;
@@ -567,8 +568,9 @@ TEST(ProtocolTypesTest, DisassembledInstruction) {
instruction.instructionBytes = "0F 1F 00";
instruction.instruction = "mov eax, ebx";
instruction.symbol = "main";
- instruction.location = Source{"test.cpp", "/path/to/test.cpp", 123,
- Source::eSourcePresentationHintNormal};
+ instruction.location =
+ Source{"test.cpp", "/path/to/test.cpp", 123,
+ Source::eSourcePresentationHintNormal, std::nullopt};
instruction.line = 10;
instruction.column = 5;
instruction.endLine = 15;
@@ -1002,3 +1004,72 @@ TEST(ProtocolTypesTest, VariablesResponseBody) {
ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
EXPECT_EQ(pp(*expected), pp(response));
}
+
+TEST(ProtocolTypesTest, CompletionItem) {
+ CompletionItem item;
+ item.label = "label";
+ item.text = "text";
+ item.sortText = "sortText";
+ item.detail = "detail";
+ item.type = eCompletionItemTypeConstructor;
+ item.start = 1;
+ item.length = 3;
+ item.selectionStart = 4;
+ item.selectionLength = 8;
+
+ const StringRef json = R"({
+ "detail": "detail",
+ "label": "label",
+ "length": 3,
+ "selectionLength": 8,
+ "selectionStart": 4,
+ "sortText": "sortText",
+ "start": 1,
+ "text": "text",
+ "type": "constructor"
+})";
+
+ EXPECT_EQ(pp(Value(item)), json);
+ EXPECT_THAT_EXPECTED(json::parse(json), HasValue(Value(item)));
+}
+
+TEST(ProtocolTypesTest, CompletionsArguments) {
+ llvm::Expected<CompletionsArguments> expected =
+ parse<CompletionsArguments>(R"({
+ "column": 8,
+ "frameId": 7,
+ "line": 9,
+ "text": "abc"
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->frameId, 7u);
+ EXPECT_EQ(expected->text, "abc");
+ EXPECT_EQ(expected->column, 8);
+ EXPECT_EQ(expected->line, 9);
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).text"));
+ EXPECT_THAT_EXPECTED(parse<CompletionsArguments>(R"({"text":"abc"})"),
+ FailedWithMessage("missing value at (root).column"));
+}
+
+TEST(ProtocolTypesTest, CompletionsResponseBody) {
+ CompletionItem item;
+ item.label = "label";
+ item.text = "text";
+ item.detail = "detail";
+ CompletionsResponseBody response{{item}};
+
+ Expected<json::Value> expected = json::parse(R"({
+ "targets": [
+ {
+ "detail": "detail",
+ "label": "label",
+ "text": "text"
+ }
+ ]
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(pp(*expected), pp(response));
+}
diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp
index 94b9559..54ac27d 100644
--- a/lldb/unittests/DAP/TestBase.cpp
+++ b/lldb/unittests/DAP/TestBase.cpp
@@ -7,17 +7,19 @@
//===----------------------------------------------------------------------===//
#include "TestBase.h"
-#include "Protocol/ProtocolBase.h"
+#include "DAPLog.h"
#include "TestingSupport/TestUtilities.h"
#include "lldb/API/SBDefines.h"
#include "lldb/API/SBStructuredData.h"
-#include "lldb/Host/File.h"
+#include "lldb/Host/MainLoop.h"
#include "lldb/Host/Pipe.h"
-#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
+#include <cstdio>
#include <memory>
+#include <system_error>
using namespace llvm;
using namespace lldb;
@@ -25,36 +27,36 @@ using namespace lldb_dap;
using namespace lldb_dap::protocol;
using namespace lldb_dap_tests;
using lldb_private::File;
-using lldb_private::NativeFile;
+using lldb_private::FileSpec;
+using lldb_private::FileSystem;
+using lldb_private::MainLoop;
using lldb_private::Pipe;
-void TransportBase::SetUp() {
- PipePairTest::SetUp();
- to_dap = std::make_unique<Transport>(
- "to_dap", nullptr,
- std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
- File::eOpenOptionReadOnly,
- NativeFile::Unowned),
- std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
- File::eOpenOptionWriteOnly,
- NativeFile::Unowned));
- from_dap = std::make_unique<Transport>(
- "from_dap", nullptr,
- std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
- File::eOpenOptionReadOnly,
- NativeFile::Unowned),
- std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
- File::eOpenOptionWriteOnly,
- NativeFile::Unowned));
+Expected<MainLoop::ReadHandleUP>
+TestTransport::RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) {
+ Expected<lldb::FileUP> dummy_file = FileSystem::Instance().Open(
+ FileSpec(FileSystem::DEV_NULL), File::eOpenOptionReadWrite);
+ if (!dummy_file)
+ return dummy_file.takeError();
+ m_dummy_file = std::move(*dummy_file);
+ lldb_private::Status status;
+ auto handle = loop.RegisterReadObject(
+ m_dummy_file, [](lldb_private::MainLoopBase &) {}, status);
+ if (status.Fail())
+ return status.takeError();
+ return handle;
}
void DAPTestBase::SetUp() {
TransportBase::SetUp();
+ std::error_code EC;
+ log = std::make_unique<Log>("-", EC);
dap = std::make_unique<DAP>(
- /*log=*/nullptr,
+ /*log=*/log.get(),
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/std::vector<std::string>(),
- /*transport=*/*to_dap);
+ /*client_name=*/"test_client",
+ /*transport=*/*transport, /*loop=*/loop);
}
void DAPTestBase::TearDown() {
@@ -72,7 +74,7 @@ void DAPTestBase::SetUpTestSuite() {
}
void DAPTestBase::TeatUpTestSuite() { SBDebugger::Terminate(); }
-bool DAPTestBase::GetDebuggerSupportsTarget(llvm::StringRef platform) {
+bool DAPTestBase::GetDebuggerSupportsTarget(StringRef platform) {
EXPECT_TRUE(dap->debugger);
lldb::SBStructuredData data = dap->debugger.GetBuildConfiguration()
@@ -81,7 +83,7 @@ bool DAPTestBase::GetDebuggerSupportsTarget(llvm::StringRef platform) {
for (size_t i = 0; i < data.GetSize(); i++) {
char buf[100] = {0};
size_t size = data.GetItemAtIndex(i).GetStringValue(buf, sizeof(buf));
- if (llvm::StringRef(buf, size) == platform)
+ if (StringRef(buf, size) == platform)
return true;
}
@@ -91,6 +93,24 @@ bool DAPTestBase::GetDebuggerSupportsTarget(llvm::StringRef platform) {
void DAPTestBase::CreateDebugger() {
dap->debugger = lldb::SBDebugger::Create();
ASSERT_TRUE(dap->debugger);
+ dap->target = dap->debugger.GetDummyTarget();
+
+ Expected<lldb::FileUP> dev_null = FileSystem::Instance().Open(
+ FileSpec(FileSystem::DEV_NULL), File::eOpenOptionReadWrite);
+ ASSERT_THAT_EXPECTED(dev_null, Succeeded());
+ lldb::FileSP dev_null_sp = std::move(*dev_null);
+
+ std::FILE *dev_null_stream = dev_null_sp->GetStream();
+ ASSERT_THAT_ERROR(dap->ConfigureIO(dev_null_stream, dev_null_stream),
+ Succeeded());
+
+ dap->debugger.SetInputFile(dap->in);
+ auto out_fd = dap->out.GetWriteFileDescriptor();
+ ASSERT_THAT_EXPECTED(out_fd, Succeeded());
+ dap->debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false));
+ auto err_fd = dap->out.GetWriteFileDescriptor();
+ ASSERT_THAT_EXPECTED(err_fd, Succeeded());
+ dap->debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false));
}
void DAPTestBase::LoadCore() {
@@ -114,18 +134,3 @@ void DAPTestBase::LoadCore() {
SBProcess process = dap->target.LoadCore(this->core->TmpName.data());
ASSERT_TRUE(process);
}
-
-std::vector<Message> DAPTestBase::DrainOutput() {
- std::vector<Message> msgs;
- output.CloseWriteFileDescriptor();
- while (true) {
- Expected<Message> next =
- from_dap->Read<protocol::Message>(std::chrono::milliseconds(1));
- if (!next) {
- consumeError(next.takeError());
- break;
- }
- msgs.push_back(*next);
- }
- return msgs;
-}
diff --git a/lldb/unittests/DAP/TestBase.h b/lldb/unittests/DAP/TestBase.h
index 50884b1..c19eead 100644
--- a/lldb/unittests/DAP/TestBase.h
+++ b/lldb/unittests/DAP/TestBase.h
@@ -8,35 +8,109 @@
#include "DAP.h"
#include "Protocol/ProtocolBase.h"
-#include "TestingSupport/Host/PipeTestUtilities.h"
-#include "Transport.h"
+#include "TestingSupport/Host/JSONTransportTestUtilities.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/lldb-forward.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <memory>
namespace lldb_dap_tests {
+class TestTransport final
+ : public lldb_private::Transport<lldb_dap::protocol::Request,
+ lldb_dap::protocol::Response,
+ lldb_dap::protocol::Event> {
+public:
+ using Message = lldb_private::Transport<lldb_dap::protocol::Request,
+ lldb_dap::protocol::Response,
+ lldb_dap::protocol::Event>::Message;
+
+ TestTransport(lldb_private::MainLoop &loop, MessageHandler &handler)
+ : m_loop(loop), m_handler(handler) {}
+
+ llvm::Error Send(const lldb_dap::protocol::Event &e) override {
+ m_loop.AddPendingCallback([this, e](lldb_private::MainLoopBase &) {
+ this->m_handler.Received(e);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Error Send(const lldb_dap::protocol::Request &r) override {
+ m_loop.AddPendingCallback([this, r](lldb_private::MainLoopBase &) {
+ this->m_handler.Received(r);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Error Send(const lldb_dap::protocol::Response &r) override {
+ m_loop.AddPendingCallback([this, r](lldb_private::MainLoopBase &) {
+ this->m_handler.Received(r);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Expected<lldb_private::MainLoop::ReadHandleUP>
+ RegisterMessageHandler(lldb_private::MainLoop &loop,
+ MessageHandler &handler) override;
+
+ void Log(llvm::StringRef message) override {
+ log_messages.emplace_back(message);
+ }
+
+ std::vector<std::string> log_messages;
+
+private:
+ lldb_private::MainLoop &m_loop;
+ MessageHandler &m_handler;
+ lldb::FileSP m_dummy_file;
+};
+
/// A base class for tests that need transport configured for communicating DAP
/// messages.
-class TransportBase : public PipePairTest {
+class TransportBase : public testing::Test {
protected:
- std::unique_ptr<lldb_dap::Transport> to_dap;
- std::unique_ptr<lldb_dap::Transport> from_dap;
+ lldb_private::SubsystemRAII<lldb_private::FileSystem, lldb_private::HostInfo>
+ subsystems;
+ lldb_private::MainLoop loop;
+ std::unique_ptr<TestTransport> transport;
+ MockMessageHandler<lldb_dap::protocol::Request, lldb_dap::protocol::Response,
+ lldb_dap::protocol::Event>
+ client;
- void SetUp() override;
+ void SetUp() override {
+ transport = std::make_unique<TestTransport>(loop, client);
+ }
};
+/// A matcher for a DAP event.
+template <typename M1, typename M2>
+inline testing::Matcher<const lldb_dap::protocol::Event &>
+IsEvent(const M1 &m1, const M2 &m2) {
+ return testing::AllOf(testing::Field(&lldb_dap::protocol::Event::event, m1),
+ testing::Field(&lldb_dap::protocol::Event::body, m2));
+}
+
/// Matches an "output" event.
-inline auto OutputMatcher(const llvm::StringRef output,
- const llvm::StringRef category = "console") {
- return testing::VariantWith<lldb_dap::protocol::Event>(testing::FieldsAre(
- /*event=*/"output", /*body=*/testing::Optional<llvm::json::Value>(
- llvm::json::Object{{"category", category}, {"output", output}})));
+inline auto Output(llvm::StringRef o, llvm::StringRef cat = "console") {
+ return IsEvent("output",
+ testing::Optional(llvm::json::Value(
+ llvm::json::Object{{"category", cat}, {"output", o}})));
}
/// A base class for tests that interact with a `lldb_dap::DAP` instance.
class DAPTestBase : public TransportBase {
protected:
+ std::unique_ptr<lldb_dap::Log> log;
std::unique_ptr<lldb_dap::DAP> dap;
std::optional<llvm::sys::fs::TempFile> core;
std::optional<llvm::sys::fs::TempFile> binary;
@@ -53,9 +127,11 @@ protected:
void CreateDebugger();
void LoadCore();
- /// Closes the DAP output pipe and returns the remaining protocol messages in
- /// the buffer.
- std::vector<lldb_dap::protocol::Message> DrainOutput();
+ void RunOnce() {
+ loop.AddPendingCallback(
+ [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+ ASSERT_THAT_ERROR(dap->Loop(), llvm::Succeeded());
+ }
};
} // namespace lldb_dap_tests
diff --git a/lldb/unittests/Editline/EditlineTest.cpp b/lldb/unittests/Editline/EditlineTest.cpp
index 6c5a0c9..2875f4e 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/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 3008fc0..5a5d3ab 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -125,8 +125,8 @@ public:
}
private:
- RegisterInfo m_reg_info;
- RegisterValue m_reg_value;
+ RegisterInfo m_reg_info{};
+ RegisterValue m_reg_value{};
};
} // namespace
diff --git a/lldb/unittests/Host/JSONTransportTest.cpp b/lldb/unittests/Host/JSONTransportTest.cpp
index 2f08464..445674f4 100644
--- a/lldb/unittests/Host/JSONTransportTest.cpp
+++ b/lldb/unittests/Host/JSONTransportTest.cpp
@@ -1,4 +1,4 @@
-//===-- JSONTransportTest.cpp ---------------------------------------------===//
+//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,16 +7,143 @@
//===----------------------------------------------------------------------===//
#include "lldb/Host/JSONTransport.h"
+#include "TestingSupport/Host/JSONTransportTestUtilities.h"
#include "TestingSupport/Host/PipeTestUtilities.h"
#include "lldb/Host/File.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Host/MainLoopBase.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <chrono>
+#include <cstddef>
+#include <memory>
+#include <string>
using namespace llvm;
using namespace lldb_private;
+using testing::_;
+using testing::HasSubstr;
+using testing::InSequence;
namespace {
-template <typename T> class JSONTransportTest : public PipePairTest {
+
+namespace test_protocol {
+
+struct Req {
+ std::string name;
+};
+json::Value toJSON(const Req &T) { return json::Object{{"req", T.name}}; }
+bool fromJSON(const json::Value &V, Req &T, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("req", T.name);
+}
+bool operator==(const Req &a, const Req &b) { return a.name == b.name; }
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Req &V) {
+ OS << toJSON(V);
+ return OS;
+}
+void PrintTo(const Req &message, std::ostream *os) {
+ std::string O;
+ llvm::raw_string_ostream OS(O);
+ OS << message;
+ *os << O;
+}
+
+struct Resp {
+ std::string name;
+};
+json::Value toJSON(const Resp &T) { return json::Object{{"resp", T.name}}; }
+bool fromJSON(const json::Value &V, Resp &T, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("resp", T.name);
+}
+bool operator==(const Resp &a, const Resp &b) { return a.name == b.name; }
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Resp &V) {
+ OS << toJSON(V);
+ return OS;
+}
+void PrintTo(const Resp &message, std::ostream *os) {
+ std::string O;
+ llvm::raw_string_ostream OS(O);
+ OS << message;
+ *os << O;
+}
+
+struct Evt {
+ std::string name;
+};
+json::Value toJSON(const Evt &T) { return json::Object{{"evt", T.name}}; }
+bool fromJSON(const json::Value &V, Evt &T, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("evt", T.name);
+}
+bool operator==(const Evt &a, const Evt &b) { return a.name == b.name; }
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Evt &V) {
+ OS << toJSON(V);
+ return OS;
+}
+void PrintTo(const Evt &message, std::ostream *os) {
+ std::string O;
+ llvm::raw_string_ostream OS(O);
+ OS << message;
+ *os << O;
+}
+
+using Message = std::variant<Req, Resp, Evt>;
+json::Value toJSON(const Message &msg) {
+ return std::visit([](const auto &msg) { return toJSON(msg); }, msg);
+}
+bool fromJSON(const json::Value &V, Message &msg, json::Path P) {
+ const json::Object *O = V.getAsObject();
+ if (!O) {
+ P.report("expected object");
+ return false;
+ }
+ if (O->get("req")) {
+ Req R;
+ if (!fromJSON(V, R, P))
+ return false;
+
+ msg = std::move(R);
+ return true;
+ }
+ if (O->get("resp")) {
+ Resp R;
+ if (!fromJSON(V, R, P))
+ return false;
+
+ msg = std::move(R);
+ return true;
+ }
+ if (O->get("evt")) {
+ Evt E;
+ if (!fromJSON(V, E, P))
+ return false;
+
+ msg = std::move(E);
+ return true;
+ }
+ P.report("unknown message type");
+ return false;
+}
+
+} // namespace test_protocol
+
+template <typename T, typename Req, typename Resp, typename Evt>
+class JSONTransportTest : public PipePairTest {
+
protected:
- std::unique_ptr<JSONTransport> transport;
+ MockMessageHandler<Req, Resp, Evt> message_handler;
+ std::unique_ptr<T> transport;
+ MainLoop loop;
void SetUp() override {
PipePairTest::SetUp();
@@ -28,79 +155,231 @@ protected:
File::eOpenOptionWriteOnly,
NativeFile::Unowned));
}
+
+ /// Run the transport MainLoop and return any messages received.
+ Error
+ Run(bool close_input = true,
+ std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) {
+ if (close_input) {
+ input.CloseWriteFileDescriptor();
+ EXPECT_CALL(message_handler, OnClosed()).WillOnce([this]() {
+ loop.RequestTermination();
+ });
+ }
+ loop.AddCallback(
+ [](MainLoopBase &loop) {
+ loop.RequestTermination();
+ FAIL() << "timeout";
+ },
+ timeout);
+ auto handle = transport->RegisterMessageHandler(loop, message_handler);
+ if (!handle)
+ return handle.takeError();
+
+ return loop.Run().takeError();
+ }
+
+ template <typename... Ts> void Write(Ts... args) {
+ std::string message;
+ for (const auto &arg : {args...})
+ message += Encode(arg);
+ EXPECT_THAT_EXPECTED(input.Write(message.data(), message.size()),
+ Succeeded());
+ }
+
+ virtual std::string Encode(const json::Value &) = 0;
};
-class HTTPDelimitedJSONTransportTest
- : public JSONTransportTest<HTTPDelimitedJSONTransport> {
+class TestHTTPDelimitedJSONTransport final
+ : public HTTPDelimitedJSONTransport<test_protocol::Req, test_protocol::Resp,
+ test_protocol::Evt> {
public:
- using JSONTransportTest::JSONTransportTest;
+ using HTTPDelimitedJSONTransport::HTTPDelimitedJSONTransport;
+
+ void Log(llvm::StringRef message) override {
+ log_messages.emplace_back(message);
+ }
+
+ std::vector<std::string> log_messages;
};
-class JSONRPCTransportTest : public JSONTransportTest<JSONRPCTransport> {
+class HTTPDelimitedJSONTransportTest
+ : public JSONTransportTest<TestHTTPDelimitedJSONTransport,
+ test_protocol::Req, test_protocol::Resp,
+ test_protocol::Evt> {
public:
using JSONTransportTest::JSONTransportTest;
+
+ std::string Encode(const json::Value &V) override {
+ std::string msg;
+ raw_string_ostream OS(msg);
+ OS << formatv("{0}", V);
+ return formatv("Content-Length: {0}\r\nContent-type: "
+ "text/json\r\n\r\n{1}",
+ msg.size(), msg)
+ .str();
+ }
};
-struct JSONTestType {
- std::string str;
+class TestJSONRPCTransport final
+ : public JSONRPCTransport<test_protocol::Req, test_protocol::Resp,
+ test_protocol::Evt> {
+public:
+ using JSONRPCTransport::JSONRPCTransport;
+
+ void Log(llvm::StringRef message) override {
+ log_messages.emplace_back(message);
+ }
+
+ std::vector<std::string> log_messages;
};
-llvm::json::Value toJSON(const JSONTestType &T) {
- return llvm::json::Object{{"str", T.str}};
-}
+class JSONRPCTransportTest
+ : public JSONTransportTest<TestJSONRPCTransport, test_protocol::Req,
+ test_protocol::Resp, test_protocol::Evt> {
+public:
+ using JSONTransportTest::JSONTransportTest;
+
+ std::string Encode(const json::Value &V) override {
+ std::string msg;
+ raw_string_ostream OS(msg);
+ OS << formatv("{0}\n", V);
+ return msg;
+ }
+};
-bool fromJSON(const llvm::json::Value &V, JSONTestType &T, llvm::json::Path P) {
- llvm::json::ObjectMapper O(V, P);
- return O && O.map("str", T.str);
-}
} // namespace
+// Failing on Windows, see https://github.com/llvm/llvm-project/issues/153446.
+#ifndef _WIN32
+using namespace test_protocol;
+
TEST_F(HTTPDelimitedJSONTransportTest, MalformedRequests) {
- std::string malformed_header = "COnTent-LenGth: -1{}\r\n\r\nnotjosn";
+ std::string malformed_header =
+ "COnTent-LenGth: -1\r\nContent-Type: text/json\r\n\r\nnotjosn";
ASSERT_THAT_EXPECTED(
input.Write(malformed_header.data(), malformed_header.size()),
Succeeded());
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- FailedWithMessage(
- "expected 'Content-Length: ' and got 'COnTent-LenGth: '"));
+
+ EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {
+ ASSERT_THAT_ERROR(std::move(err),
+ FailedWithMessage("invalid content length: -1"));
+ });
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
TEST_F(HTTPDelimitedJSONTransportTest, Read) {
- std::string json = R"json({"str": "foo"})json";
- std::string message =
- formatv("Content-Length: {0}\r\n\r\n{1}", json.size(), json).str();
- ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
- Succeeded());
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- HasValue(testing::FieldsAre(/*str=*/"foo")));
+ Write(Req{"foo"});
+ EXPECT_CALL(message_handler, Received(Req{"foo"}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
-TEST_F(HTTPDelimitedJSONTransportTest, ReadWithEOF) {
+TEST_F(HTTPDelimitedJSONTransportTest, ReadMultipleMessagesInSingleWrite) {
+ InSequence seq;
+ Write(Message{Req{"one"}}, Message{Evt{"two"}}, Message{Resp{"three"}});
+ EXPECT_CALL(message_handler, Received(Req{"one"}));
+ EXPECT_CALL(message_handler, Received(Evt{"two"}));
+ EXPECT_CALL(message_handler, Received(Resp{"three"}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(HTTPDelimitedJSONTransportTest, ReadAcrossMultipleChunks) {
+ std::string long_str = std::string(
+ HTTPDelimitedJSONTransport<Req, Resp, Evt>::kReadBufferSize * 2, 'x');
+ Write(Req{long_str});
+ EXPECT_CALL(message_handler, Received(Req{long_str}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(HTTPDelimitedJSONTransportTest, ReadPartialMessage) {
+ std::string message = Encode(Req{"foo"});
+ auto split_at = message.size() / 2;
+ std::string part1 = message.substr(0, split_at);
+ std::string part2 = message.substr(split_at);
+
+ EXPECT_CALL(message_handler, Received(Req{"foo"}));
+
+ ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());
+ ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());
input.CloseWriteFileDescriptor();
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportEOFError>());
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(HTTPDelimitedJSONTransportTest, ReadWithZeroByteWrites) {
+ std::string message = Encode(Req{"foo"});
+ auto split_at = message.size() / 2;
+ std::string part1 = message.substr(0, split_at);
+ std::string part2 = message.substr(split_at);
+
+ EXPECT_CALL(message_handler, Received(Req{"foo"}));
+
+ ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
+
+ // Run the main loop once for the initial read.
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());
+
+ // zero-byte write.
+ ASSERT_THAT_EXPECTED(input.Write(part1.data(), 0),
+ Succeeded()); // zero-byte write.
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());
+
+ // Write the remaining part of the message.
+ ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(HTTPDelimitedJSONTransportTest, ReadWithEOF) {
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
+TEST_F(HTTPDelimitedJSONTransportTest, ReaderWithUnhandledData) {
+ std::string json = R"json({"str": "foo"})json";
+ std::string message =
+ formatv("Content-Length: {0}\r\nContent-type: text/json\r\n\r\n{1}",
+ json.size(), json)
+ .str();
+
+ EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {
+ // The error should indicate that there are unhandled contents.
+ ASSERT_THAT_ERROR(std::move(err),
+ Failed<TransportUnhandledContentsError>());
+ });
+
+ // Write an incomplete message and close the handle.
+ ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size() - 1),
+ Succeeded());
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
TEST_F(HTTPDelimitedJSONTransportTest, InvalidTransport) {
- transport = std::make_unique<HTTPDelimitedJSONTransport>(nullptr, nullptr);
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportInvalidError>());
+ transport =
+ std::make_unique<TestHTTPDelimitedJSONTransport>(nullptr, nullptr);
+ ASSERT_THAT_ERROR(Run(/*close_input=*/false),
+ FailedWithMessage("IO object is not valid."));
}
TEST_F(HTTPDelimitedJSONTransportTest, Write) {
- ASSERT_THAT_ERROR(transport->Write(JSONTestType{"foo"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Req{"foo"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Resp{"bar"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Evt{"baz"}), Succeeded());
output.CloseWriteFileDescriptor();
char buf[1024];
Expected<size_t> bytes_read =
output.Read(buf, sizeof(buf), std::chrono::milliseconds(1));
ASSERT_THAT_EXPECTED(bytes_read, Succeeded());
ASSERT_EQ(StringRef(buf, *bytes_read), StringRef("Content-Length: 13\r\n\r\n"
- R"json({"str":"foo"})json"));
+ R"({"req":"foo"})"
+ "Content-Length: 14\r\n\r\n"
+ R"({"resp":"bar"})"
+ "Content-Length: 13\r\n\r\n"
+ R"({"evt":"baz"})"));
}
TEST_F(JSONRPCTransportTest, MalformedRequests) {
@@ -108,74 +387,94 @@ TEST_F(JSONRPCTransportTest, MalformedRequests) {
ASSERT_THAT_EXPECTED(
input.Write(malformed_header.data(), malformed_header.size()),
Succeeded());
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- llvm::Failed());
+ EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {
+ ASSERT_THAT_ERROR(std::move(err),
+ FailedWithMessage(HasSubstr("Invalid JSON value")));
+ });
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
TEST_F(JSONRPCTransportTest, Read) {
- std::string json = R"json({"str": "foo"})json";
- std::string message = formatv("{0}\n", json).str();
- ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
- Succeeded());
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- HasValue(testing::FieldsAre(/*str=*/"foo")));
+ Write(Message{Req{"foo"}});
+ EXPECT_CALL(message_handler, Received(Req{"foo"}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
-TEST_F(JSONRPCTransportTest, ReadWithEOF) {
+TEST_F(JSONRPCTransportTest, ReadMultipleMessagesInSingleWrite) {
+ InSequence seq;
+ Write(Message{Req{"one"}}, Message{Evt{"two"}}, Message{Resp{"three"}});
+ EXPECT_CALL(message_handler, Received(Req{"one"}));
+ EXPECT_CALL(message_handler, Received(Evt{"two"}));
+ EXPECT_CALL(message_handler, Received(Resp{"three"}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(JSONRPCTransportTest, ReadAcrossMultipleChunks) {
+ // Use a string longer than the chunk size to ensure we split the message
+ // across the chunk boundary.
+ std::string long_str =
+ std::string(JSONTransport<Req, Resp, Evt>::kReadBufferSize * 2, 'x');
+ Write(Req{long_str});
+ EXPECT_CALL(message_handler, Received(Req{long_str}));
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(JSONRPCTransportTest, ReadPartialMessage) {
+ std::string message = R"({"req": "foo"})"
+ "\n";
+ std::string part1 = message.substr(0, 7);
+ std::string part2 = message.substr(7);
+
+ EXPECT_CALL(message_handler, Received(Req{"foo"}));
+
+ ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ ASSERT_THAT_ERROR(Run(/*close_input=*/false), Succeeded());
+
+ ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());
input.CloseWriteFileDescriptor();
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportEOFError>());
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(JSONRPCTransportTest, ReadWithEOF) {
+ ASSERT_THAT_ERROR(Run(), Succeeded());
+}
+
+TEST_F(JSONRPCTransportTest, ReaderWithUnhandledData) {
+ std::string message = R"json({"req": "foo")json";
+ // Write an incomplete message and close the handle.
+ ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size() - 1),
+ Succeeded());
+
+ EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {
+ ASSERT_THAT_ERROR(std::move(err),
+ Failed<TransportUnhandledContentsError>());
+ });
+ ASSERT_THAT_ERROR(Run(), Succeeded());
}
TEST_F(JSONRPCTransportTest, Write) {
- ASSERT_THAT_ERROR(transport->Write(JSONTestType{"foo"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Req{"foo"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Resp{"bar"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Evt{"baz"}), Succeeded());
output.CloseWriteFileDescriptor();
char buf[1024];
Expected<size_t> bytes_read =
output.Read(buf, sizeof(buf), std::chrono::milliseconds(1));
ASSERT_THAT_EXPECTED(bytes_read, Succeeded());
- ASSERT_EQ(StringRef(buf, *bytes_read), StringRef(R"json({"str":"foo"})json"
+ ASSERT_EQ(StringRef(buf, *bytes_read), StringRef(R"({"req":"foo"})"
+ "\n"
+ R"({"resp":"bar"})"
+ "\n"
+ R"({"evt":"baz"})"
"\n"));
}
TEST_F(JSONRPCTransportTest, InvalidTransport) {
- transport = std::make_unique<JSONRPCTransport>(nullptr, nullptr);
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportInvalidError>());
-}
-
-#ifndef _WIN32
-TEST_F(HTTPDelimitedJSONTransportTest, ReadWithTimeout) {
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportTimeoutError>());
+ transport = std::make_unique<TestJSONRPCTransport>(nullptr, nullptr);
+ ASSERT_THAT_ERROR(Run(/*close_input=*/false),
+ FailedWithMessage("IO object is not valid."));
}
-TEST_F(JSONRPCTransportTest, ReadWithTimeout) {
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- Failed<TransportTimeoutError>());
-}
-
-// Windows CRT _read checks that the file descriptor is valid and calls a
-// handler if not. This handler is normally a breakpoint, which looks like a
-// crash when not handled by a debugger.
-// https://learn.microsoft.com/en-us/%20cpp/c-runtime-library/reference/read?view=msvc-170
-TEST_F(HTTPDelimitedJSONTransportTest, ReadAfterClosed) {
- input.CloseReadFileDescriptor();
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- llvm::Failed());
-}
-
-TEST_F(JSONRPCTransportTest, ReadAfterClosed) {
- input.CloseReadFileDescriptor();
- ASSERT_THAT_EXPECTED(
- transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
- llvm::Failed());
-}
#endif
diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp
index 448ad9c..0bc291c 100644
--- a/lldb/unittests/Host/MainLoopTest.cpp
+++ b/lldb/unittests/Host/MainLoopTest.cpp
@@ -80,6 +80,8 @@ TEST_F(MainLoopTest, ReadSocketObject) {
ASSERT_EQ(1u, callback_count);
}
+// Flakey, see https://github.com/llvm/llvm-project/issues/152677.
+#ifndef _WIN32
TEST_F(MainLoopTest, ReadPipeObject) {
Pipe pipe;
@@ -142,6 +144,7 @@ TEST_F(MainLoopTest, MultipleReadsPipeObject) {
ASSERT_EQ(5u, callback_count);
async_writer.wait();
}
+#endif
TEST_F(MainLoopTest, PipeDelayBetweenRegisterAndRun) {
Pipe pipe;
diff --git a/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp b/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
index 4506c20..c6bfe0f 100644
--- a/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
+++ b/lldb/unittests/Instruction/ARM64/TestAArch64Emulator.cpp
@@ -13,15 +13,118 @@
#include "lldb/Core/Disassembler.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/RegisterValue.h"
#include "Plugins/Instruction/ARM64/EmulateInstructionARM64.h"
+#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
+#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
using namespace lldb;
using namespace lldb_private;
struct Arch64EmulatorTester : public EmulateInstructionARM64 {
+ RegisterInfoPOSIX_arm64::GPR gpr;
+ uint8_t memory[64] = {0};
+ uint64_t memory_offset = 0;
+
Arch64EmulatorTester()
- : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {}
+ : EmulateInstructionARM64(ArchSpec("arm64-apple-ios")) {
+ memset(&gpr, 0, sizeof(gpr));
+ EmulateInstruction::SetCallbacks(ReadMemoryCallback, WriteMemoryCallback,
+ ReadRegisterCallback,
+ WriteRegisterCallback);
+ }
+
+ static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
+ const RegisterInfo *reg_info,
+ RegisterValue &reg_value) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (reg >= gpr_x0_arm64 && reg <= gpr_x28_arm64) {
+ reg_value.SetUInt64(tester->gpr.x[reg - gpr_x0_arm64]);
+ return true;
+ }
+ if (reg >= gpr_w0_arm64 && reg <= gpr_w28_arm64) {
+ reg_value.SetUInt32(tester->gpr.x[reg - gpr_w0_arm64]);
+ return true;
+ }
+ switch (reg) {
+ case gpr_fp_arm64:
+ reg_value.SetUInt64(tester->gpr.fp);
+ return true;
+ case gpr_lr_arm64:
+ reg_value.SetUInt64(tester->gpr.lr);
+ return true;
+ case gpr_sp_arm64:
+ reg_value.SetUInt64(tester->gpr.sp);
+ return true;
+ case gpr_pc_arm64:
+ reg_value.SetUInt64(tester->gpr.pc);
+ return true;
+ case gpr_cpsr_arm64:
+ reg_value.SetUInt32(tester->gpr.cpsr);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool WriteRegisterCallback(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ const RegisterInfo *reg_info,
+ const RegisterValue &reg_value) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ uint32_t reg = reg_info->kinds[eRegisterKindLLDB];
+ if (reg >= gpr_x0_arm64 && reg <= gpr_x28_arm64) {
+ tester->gpr.x[reg - gpr_x0_arm64] = reg_value.GetAsUInt64();
+ return true;
+ }
+ if (reg >= gpr_w0_arm64 && reg <= gpr_w28_arm64) {
+ tester->gpr.x[reg - gpr_w0_arm64] = reg_value.GetAsUInt32();
+ return true;
+ }
+ switch (reg) {
+ case gpr_fp_arm64:
+ tester->gpr.fp = reg_value.GetAsUInt64();
+ return true;
+ case gpr_lr_arm64:
+ tester->gpr.lr = reg_value.GetAsUInt64();
+ return true;
+ case gpr_sp_arm64:
+ tester->gpr.sp = reg_value.GetAsUInt64();
+ return true;
+ case gpr_pc_arm64:
+ tester->gpr.pc = reg_value.GetAsUInt64();
+ return true;
+ case gpr_cpsr_arm64:
+ tester->gpr.cpsr = reg_value.GetAsUInt32();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
+ const Context &context, addr_t addr,
+ void *dst, size_t length) {
+ auto *tester = static_cast<Arch64EmulatorTester *>(instruction);
+ assert(addr >= tester->memory_offset);
+ assert(addr - tester->memory_offset + length <= sizeof(tester->memory));
+ if (addr >= tester->memory_offset &&
+ addr - tester->memory_offset + length <= sizeof(tester->memory)) {
+ memcpy(dst, tester->memory + (addr - tester->memory_offset), length);
+ return length;
+ }
+ return 0;
+ };
+
+ static size_t WriteMemoryCallback(EmulateInstruction *instruction,
+ void *baton, const Context &context,
+ addr_t addr, const void *dst,
+ size_t length) {
+ llvm_unreachable("implement when required");
+ return 0;
+ };
static uint64_t AddWithCarry(uint32_t N, uint64_t x, uint64_t y, bool carry_in,
EmulateInstructionARM64::ProcState &proc_state) {
@@ -60,3 +163,18 @@ TEST_F(TestAArch64Emulator, TestOverflow) {
ASSERT_EQ(pstate.V, 1ULL);
ASSERT_EQ(pstate.C, 0ULL);
}
+
+TEST_F(TestAArch64Emulator, TestAutoAdvancePC) {
+ Arch64EmulatorTester emu;
+ emu.memory_offset = 0x123456789abcde00;
+ emu.gpr.pc = 0x123456789abcde00;
+ emu.gpr.x[8] = 0x123456789abcde20;
+ memcpy(emu.memory, "\x08\x01\x40\xb9", 4); // ldr w8, [x8]
+ memcpy(emu.memory + 0x20, "\x11\x22\x33\x44", 4); // 0x44332211
+ ASSERT_TRUE(emu.ReadInstruction());
+ ASSERT_TRUE(
+ emu.EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC |
+ eEmulateInstructionOptionIgnoreConditions));
+ ASSERT_EQ(emu.gpr.pc, (uint64_t)0x123456789abcde04);
+ ASSERT_EQ(emu.gpr.x[8], (uint64_t)0x44332211);
+}
diff --git a/lldb/unittests/Language/CPlusPlus/CMakeLists.txt b/lldb/unittests/Language/CPlusPlus/CMakeLists.txt
index 4882eaf..1d96fcf 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 6eeb4f5..8c36f54 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>
@@ -397,3 +399,206 @@ TEST(CPlusPlusLanguage, CPlusPlusNameParser) {
// Don't crash.
CPlusPlusNameParser((const char *)nullptr);
}
+
+TEST(CPlusPlusLanguage, DoesNotMatchCxx) {
+ // Test that a symbol name that is NOT C++ does not match C++.
+
+ SubsystemRAII<CPlusPlusLanguage> lang;
+ Language *CPlusPlusLang =
+ Language::FindPlugin(lldb::eLanguageTypeC_plus_plus);
+
+ EXPECT_TRUE(CPlusPlusLang != nullptr);
+
+ Mangled swiftSymbol("$sS");
+ EXPECT_FALSE(CPlusPlusLang->SymbolNameFitsToLanguage(swiftSymbol));
+}
+
+TEST(CPlusPlusLanguage, MatchesCxx) {
+ // Test that a symbol name that is C++ does match C++ (both Itanium and MSVC).
+
+ SubsystemRAII<CPlusPlusLanguage> lang;
+ Language *CPlusPlusLang =
+ Language::FindPlugin(lldb::eLanguageTypeC_plus_plus);
+
+ EXPECT_TRUE(CPlusPlusLang != nullptr);
+
+ Mangled itaniumSymbol("_Z3Foo");
+ EXPECT_TRUE(CPlusPlusLang->SymbolNameFitsToLanguage(itaniumSymbol));
+ Mangled itaniumExtensionSymbol("___Z3Bar_block_invoke");
+ EXPECT_TRUE(CPlusPlusLang->SymbolNameFitsToLanguage(itaniumExtensionSymbol));
+ 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 80abc5b..4113878 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 bbac696..f877517 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 0000000..9fa4461
--- /dev/null
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -0,0 +1,304 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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 TestMCPTransport final : public MCPTransport {
+public:
+ TestMCPTransport(lldb::IOObjectSP in, lldb::IOObjectSP out)
+ : lldb_protocol::mcp::MCPTransport(in, out, "unittest") {}
+
+ using MCPTransport::Write;
+
+ void Log(llvm::StringRef message) override {
+ log_messages.emplace_back(message);
+ }
+
+ std::vector<std::string> log_messages;
+};
+
+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<TestMCPTransport> 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<TestMCPTransport>(
+ 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<TestMCPTransport>(
+ 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 1dca0e5..396e361 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());
@@ -149,9 +135,7 @@ TEST(ProtocolMCPTest, MessageWithRequest) {
const Request &deserialized_request =
std::get<Request>(*deserialized_message);
- EXPECT_EQ(request.id, deserialized_request.id);
- EXPECT_EQ(request.method, deserialized_request.method);
- EXPECT_EQ(request.params, deserialized_request.params);
+ EXPECT_EQ(request, deserialized_request);
}
TEST(ProtocolMCPTest, MessageWithResponse) {
@@ -168,8 +152,7 @@ TEST(ProtocolMCPTest, MessageWithResponse) {
const Response &deserialized_response =
std::get<Response>(*deserialized_message);
- EXPECT_EQ(response.id, deserialized_response.id);
- EXPECT_EQ(response.result, deserialized_response.result);
+ EXPECT_EQ(response, deserialized_response);
}
TEST(ProtocolMCPTest, MessageWithNotification) {
@@ -186,49 +169,28 @@ TEST(ProtocolMCPTest, MessageWithNotification) {
const Notification &deserialized_notification =
std::get<Notification>(*deserialized_message);
- EXPECT_EQ(notification.method, deserialized_notification.method);
- EXPECT_EQ(notification.params, deserialized_notification.params);
+ EXPECT_EQ(notification, deserialized_notification);
}
-TEST(ProtocolMCPTest, MessageWithError) {
- ErrorInfo error_info;
- error_info.code = -32603;
- error_info.message = "Internal error";
-
+TEST(ProtocolMCPTest, MessageWithErrorResponse) {
Error error;
- error.id = 3;
- error.error = error_info;
+ error.code = -32603;
+ error.message = "Internal error";
+
+ Response error_response;
+ error_response.id = 3;
+ error_response.result = error;
- Message message = error;
+ Message message = error_response;
llvm::Expected<Message> deserialized_message = roundtripJSON(message);
ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
- ASSERT_TRUE(std::holds_alternative<Error>(*deserialized_message));
- const Error &deserialized_error = std::get<Error>(*deserialized_message);
-
- EXPECT_EQ(error.id, deserialized_error.id);
- EXPECT_EQ(error.error.code, deserialized_error.error.code);
- EXPECT_EQ(error.error.message, deserialized_error.error.message);
-}
-
-TEST(ProtocolMCPTest, ResponseWithError) {
- ErrorInfo error_info;
- error_info.code = -32700;
- error_info.message = "Parse error";
-
- Response response;
- response.id = 4;
- response.error = error_info;
-
- llvm::Expected<Response> deserialized_response = roundtripJSON(response);
- ASSERT_THAT_EXPECTED(deserialized_response, llvm::Succeeded());
+ ASSERT_TRUE(std::holds_alternative<Response>(*deserialized_message));
+ const Response &deserialized_error =
+ std::get<Response>(*deserialized_message);
- EXPECT_EQ(response.id, deserialized_response->id);
- EXPECT_FALSE(deserialized_response->result.has_value());
- ASSERT_TRUE(deserialized_response->error.has_value());
- EXPECT_EQ(response.error->code, deserialized_response->error->code);
- EXPECT_EQ(response.error->message, deserialized_response->error->message);
+ EXPECT_EQ(error_response, deserialized_error);
}
TEST(ProtocolMCPTest, Resource) {
@@ -261,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());
@@ -276,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());
@@ -290,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());
@@ -320,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 0000000..f8a14f4
--- /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 6117430..0000000
--- 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 dc3b63a..0000000
--- a/lldb/unittests/ProtocolServer/ProtocolMCPServerTest.cpp
+++ /dev/null
@@ -1,325 +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/SocketTestUtilities.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/Socket.h"
-#include "lldb/Protocol/MCP/MCPError.h"
-#include "lldb/Protocol/MCP/Protocol.h"
-#include "llvm/Testing/Support/Error.h"
-#include "gtest/gtest.h"
-
-using namespace llvm;
-using namespace lldb;
-using namespace lldb_private;
-using namespace lldb_protocol::mcp;
-
-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;
-};
-
-class TestJSONTransport : public lldb_private::JSONRPCTransport {
-public:
- using JSONRPCTransport::JSONRPCTransport;
- using JSONRPCTransport::ReadImpl;
- using JSONRPCTransport::WriteImpl;
-};
-
-/// Test tool that returns it argument as text.
-class TestTool : public Tool {
-public:
- using Tool::Tool;
-
- virtual 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;
-
- virtual 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;
- }
-
- virtual 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;
-
- virtual 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;
-
- virtual 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;
-
- static constexpr llvm::StringLiteral k_localhost = "localhost";
-
- llvm::Error Write(llvm::StringRef message) {
- return m_transport_up->WriteImpl(llvm::formatv("{0}\n", message).str());
- }
-
- llvm::Expected<std::string> Read() {
- return m_transport_up->ReadImpl(std::chrono::milliseconds(100));
- }
-
- void SetUp() {
- // 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() {
- // Stop the server.
- ASSERT_THAT_ERROR(m_server_up->Stop(), llvm::Succeeded());
- }
-};
-
-} // namespace
-
-TEST_F(ProtocolServerMCPTest, Intialization) {
- 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":0})json";
- llvm::StringLiteral response =
- R"json( {"id":0,"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), llvm::Succeeded());
-
- llvm::Expected<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-TEST_F(ProtocolServerMCPTest, ToolsList) {
- llvm::StringLiteral request =
- R"json({"method":"tools/list","params":{},"jsonrpc":"2.0","id":1})json";
- llvm::StringLiteral response =
- R"json({"id":1,"jsonrpc":"2.0","result":{"tools":[{"description":"test tool","inputSchema":{"type":"object"},"name":"test"},{"description":"Run an lldb command.","inputSchema":{"properties":{"arguments":{"type":"string"},"debugger_id":{"type":"number"}},"required":["debugger_id"],"type":"object"},"name":"lldb_command"}]}})json";
-
- ASSERT_THAT_ERROR(Write(request), llvm::Succeeded());
-
- llvm::Expected<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-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<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-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<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-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<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-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<std::string> response_str = Read();
- ASSERT_THAT_EXPECTED(response_str, llvm::Succeeded());
-
- llvm::Expected<json::Value> response_json = json::parse(*response_str);
- ASSERT_THAT_EXPECTED(response_json, llvm::Succeeded());
-
- llvm::Expected<json::Value> expected_json = json::parse(response);
- ASSERT_THAT_EXPECTED(expected_json, llvm::Succeeded());
-
- EXPECT_EQ(*response_json, *expected_json);
-}
-
-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/TestingSupport/Host/JSONTransportTestUtilities.h b/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h
new file mode 100644
index 0000000..5a9eb8e
--- /dev/null
+++ b/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H
+#define LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H
+
+#include "lldb/Host/JSONTransport.h"
+#include "gmock/gmock.h"
+
+template <typename Req, typename Resp, typename Evt>
+class MockMessageHandler final
+ : public lldb_private::Transport<Req, Resp, Evt>::MessageHandler {
+public:
+ MOCK_METHOD(void, Received, (const Evt &), (override));
+ MOCK_METHOD(void, Received, (const Req &), (override));
+ MOCK_METHOD(void, Received, (const Resp &), (override));
+ MOCK_METHOD(void, OnError, (llvm::Error), (override));
+ MOCK_METHOD(void, OnClosed, (), (override));
+};
+
+#endif
diff --git a/lldb/unittests/TestingSupport/TestUtilities.h b/lldb/unittests/TestingSupport/TestUtilities.h
index db62881..cc93a68 100644
--- a/lldb/unittests/TestingSupport/TestUtilities.h
+++ b/lldb/unittests/TestingSupport/TestUtilities.h
@@ -11,11 +11,11 @@
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Utility/DataBuffer.h"
-#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
#include <string>
#define ASSERT_NO_ERROR(x) \
@@ -61,12 +61,10 @@ private:
};
template <typename T> static llvm::Expected<T> roundtripJSON(const T &input) {
- llvm::json::Value value = toJSON(input);
- llvm::json::Path::Root root;
- T output;
- if (!fromJSON(value, output, root))
- return root.getError();
- return output;
+ std::string encoded;
+ llvm::raw_string_ostream OS(encoded);
+ OS << toJSON(input);
+ return llvm::json::parse<T>(encoded);
}
} // namespace lldb_private
diff --git a/lldb/unittests/Utility/ScalarTest.cpp b/lldb/unittests/Utility/ScalarTest.cpp
index 65b9783..256d456 100644
--- a/lldb/unittests/Utility/ScalarTest.cpp
+++ b/lldb/unittests/Utility/ScalarTest.cpp
@@ -191,6 +191,24 @@ TEST(ScalarTest, GetData) {
EXPECT_THAT(
get_data(llvm::APSInt::getMaxValue(/*numBits=*/9, /*Unsigned=*/true)),
vec({0x01, 0xff}));
+
+ auto get_data_with_size = [](llvm::APInt v, size_t size) {
+ DataExtractor data;
+ Scalar(v).GetData(data, size);
+ return data.GetData().vec();
+ };
+
+ EXPECT_THAT(get_data_with_size(llvm::APInt(16, 0x0123), 8),
+ vec({0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x23}));
+
+ EXPECT_THAT(get_data_with_size(llvm::APInt(32, 0x01234567), 4),
+ vec({0x01, 0x23, 0x45, 0x67}));
+
+ EXPECT_THAT(get_data_with_size(llvm::APInt(48, 0xABCD01234567UL), 4),
+ vec({0x01, 0x23, 0x45, 0x67}));
+
+ EXPECT_THAT(get_data_with_size(llvm::APInt(64, 0xABCDEF0123456789UL), 2),
+ vec({0x67, 0x89}));
}
TEST(ScalarTest, SetValueFromData) {
diff --git a/lldb/unittests/Utility/XcodeSDKTest.cpp b/lldb/unittests/Utility/XcodeSDKTest.cpp
index 4db6a50..de9f91a 100644
--- a/lldb/unittests/Utility/XcodeSDKTest.cpp
+++ b/lldb/unittests/Utility/XcodeSDKTest.cpp
@@ -102,17 +102,6 @@ TEST(XcodeSDKTest, SDKSupportsModules) {
}
#endif
-TEST(XcodeSDKTest, SDKSupportsSwift) {
- EXPECT_TRUE(XcodeSDK("iPhoneSimulator12.0.sdk").SupportsSwift());
- EXPECT_TRUE(XcodeSDK("iPhoneSimulator12.0.Internal.sdk").SupportsSwift());
- EXPECT_FALSE(XcodeSDK("iPhoneSimulator7.2.sdk").SupportsSwift());
- EXPECT_TRUE(XcodeSDK("MacOSX10.10.sdk").SupportsSwift());
- EXPECT_FALSE(XcodeSDK("MacOSX10.9.sdk").SupportsSwift());
- EXPECT_TRUE(XcodeSDK("Linux.sdk").SupportsSwift());
- EXPECT_TRUE(XcodeSDK("MacOSX.sdk").SupportsSwift());
- EXPECT_FALSE(XcodeSDK("EverythingElse.sdk").SupportsSwift());
-}
-
TEST(XcodeSDKTest, GetCanonicalNameAndConstruct) {
XcodeSDK::Info info;
info.type = XcodeSDK::Type::MacOSX;
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
index f65034c..5c78b82 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 2507910..e6d5b7b 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
diff --git a/lldb/utils/lui/lldbutil.py b/lldb/utils/lui/lldbutil.py
index 6cbf4a3..140317a 100644
--- a/lldb/utils/lui/lldbutil.py
+++ b/lldb/utils/lui/lldbutil.py
@@ -1040,8 +1040,8 @@ class ChildVisitingFormatter(BasicFormatter):
return output.getvalue()
-class RecursiveDecentFormatter(BasicFormatter):
- """The recursive decent formatter prints the value and the decendents.
+class RecursiveDescentFormatter(BasicFormatter):
+ """The recursive descent formatter prints the value and the descendents.
The constructor takes two keyword args: indent_level, which defaults to 0,
and indent_child, which defaults to 2. The current indentation level is
@@ -1058,7 +1058,6 @@ class RecursiveDecentFormatter(BasicFormatter):
output = io.StringIO()
else:
output = buffer
-
BasicFormatter.format(self, value, buffer=output, indent=self.lindent)
new_indent = self.lindent + self.cindent
for child in value:
@@ -1066,7 +1065,7 @@ class RecursiveDecentFormatter(BasicFormatter):
BasicFormatter.format(self, child, buffer=output, indent=new_indent)
else:
if child.GetNumChildren() > 0:
- rdf = RecursiveDecentFormatter(indent_level=new_indent)
+ rdf = RecursiveDescentFormatter(indent_level=new_indent)
rdf.format(child, buffer=output)
else:
BasicFormatter.format(self, child, buffer=output, indent=new_indent)