summaryrefslogtreecommitdiff
path: root/lldb
diff options
context:
space:
mode:
authorOliver Hunt <oliver@apple.com>2025-10-20 01:38:07 -0700
committerGitHub <noreply@github.com>2025-10-20 01:38:07 -0700
commit7de01aa5d0418bd4e8db2917f831e7383c6863bb (patch)
tree1db866f57c2236573cd4b4c2d141d6d420f87a92 /lldb
parent6bc540043d4c3fed8f44c8f6de86be0d1740582e (diff)
parent46a866ab7735aaa0f89fde209d516271c4825c49 (diff)
Merge branch 'main' into users/ojhunt/ptrauth-additionsusers/ojhunt/ptrauth-additions
Diffstat (limited to 'lldb')
-rw-r--r--lldb/bindings/python/python-swigsafecast.swig4
-rw-r--r--lldb/bindings/python/python-typemaps.swig5
-rw-r--r--lldb/bindings/python/python-wrapper.swig24
-rw-r--r--lldb/cmake/modules/AddLLDB.cmake18
-rw-r--r--lldb/cmake/modules/LLDBFramework.cmake2
-rw-r--r--lldb/docs/resources/extensions.rst2
-rw-r--r--lldb/docs/resources/lldbgdbremote.md128
-rw-r--r--lldb/docs/use/python-reference.rst2
-rw-r--r--lldb/docs/use/tutorials/creating-custom-breakpoints.md46
-rw-r--r--lldb/examples/python/lldbtk.py8
-rwxr-xr-xlldb/examples/python/performance.py9
-rw-r--r--lldb/examples/summaries/cocoa/CFString.py7
-rw-r--r--lldb/include/lldb/API/SBBreakpoint.h8
-rw-r--r--lldb/include/lldb/API/SBBreakpointLocation.h2
-rw-r--r--lldb/include/lldb/API/SBDebugger.h3
-rw-r--r--lldb/include/lldb/API/SBFrame.h1
-rw-r--r--lldb/include/lldb/API/SBTarget.h8
-rw-r--r--lldb/include/lldb/Breakpoint/Breakpoint.h59
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointLocation.h106
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h3
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointLocationList.h3
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h7
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointSite.h3
-rw-r--r--lldb/include/lldb/Breakpoint/StopPointSiteList.h24
-rw-r--r--lldb/include/lldb/Breakpoint/StoppointSite.h7
-rw-r--r--lldb/include/lldb/Core/Debugger.h1
-rw-r--r--lldb/include/lldb/Core/Mangled.h10
-rw-r--r--lldb/include/lldb/Core/ModuleList.h7
-rw-r--r--lldb/include/lldb/Host/JSONTransport.h627
-rw-r--r--lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h10
-rw-r--r--lldb/include/lldb/Interpreter/ScriptInterpreter.h6
-rw-r--r--lldb/include/lldb/Protocol/MCP/MCPError.h5
-rw-r--r--lldb/include/lldb/Protocol/MCP/Protocol.h5
-rw-r--r--lldb/include/lldb/Protocol/MCP/Server.h72
-rw-r--r--lldb/include/lldb/Protocol/MCP/Transport.h60
-rw-r--r--lldb/include/lldb/Target/Language.h12
-rw-r--r--lldb/include/lldb/Target/Statistics.h2
-rw-r--r--lldb/include/lldb/Target/Target.h14
-rw-r--r--lldb/include/lldb/Target/TargetList.h11
-rw-r--r--lldb/include/lldb/Utility/AnsiTerminal.h22
-rw-r--r--lldb/include/lldb/Utility/DataExtractor.h2
-rw-r--r--lldb/include/lldb/Utility/Stream.h12
-rw-r--r--lldb/include/lldb/Utility/XcodeSDK.h2
-rw-r--r--lldb/include/lldb/lldb-defines.h1
-rw-r--r--lldb/include/lldb/lldb-enumerations.h2
-rw-r--r--lldb/packages/Python/lldbsuite/support/seven.py6
-rw-r--r--lldb/packages/Python/lldbsuite/test/cpu_feature.py2
-rw-r--r--lldb/packages/Python/lldbsuite/test/dotest.py9
-rw-r--r--lldb/packages/Python/lldbsuite/test/gdbclientutils.py71
-rw-r--r--lldb/packages/Python/lldbsuite/test/lldbtest.py4
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py55
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py79
-rw-r--r--lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py12
-rw-r--r--lldb/packages/Python/lldbsuite/test_event/build_exception.py4
-rw-r--r--lldb/source/API/SBBreakpoint.cpp20
-rw-r--r--lldb/source/API/SBBreakpointLocation.cpp11
-rw-r--r--lldb/source/API/SBDebugger.cpp11
-rw-r--r--lldb/source/API/SBTarget.cpp9
-rw-r--r--lldb/source/Breakpoint/Breakpoint.cpp94
-rw-r--r--lldb/source/Breakpoint/BreakpointLocation.cpp176
-rw-r--r--lldb/source/Breakpoint/BreakpointLocationCollection.cpp16
-rw-r--r--lldb/source/Breakpoint/BreakpointLocationList.cpp5
-rw-r--r--lldb/source/Breakpoint/BreakpointResolverName.cpp2
-rw-r--r--lldb/source/Breakpoint/BreakpointResolverScripted.cpp23
-rw-r--r--lldb/source/Breakpoint/BreakpointSite.cpp6
-rw-r--r--lldb/source/Commands/CommandObjectProtocolServer.cpp44
-rw-r--r--lldb/source/Commands/CommandObjectTarget.cpp6
-rw-r--r--lldb/source/Commands/CommandObjectType.cpp4
-rw-r--r--lldb/source/Commands/CommandOptionsProcessLaunch.cpp4
-rw-r--r--lldb/source/Commands/Options.td5
-rw-r--r--lldb/source/Core/CoreProperties.td10
-rw-r--r--lldb/source/Core/Debugger.cpp49
-rw-r--r--lldb/source/Core/IOHandler.cpp11
-rw-r--r--lldb/source/Core/Mangled.cpp6
-rw-r--r--lldb/source/Core/ModuleList.cpp72
-rw-r--r--lldb/source/Expression/IRExecutionUnit.cpp7
-rw-r--r--lldb/source/Host/common/JSONTransport.cpp26
-rw-r--r--lldb/source/Host/freebsd/Host.cpp40
-rw-r--r--lldb/source/Host/macosx/objcxx/Host.mm32
-rw-r--r--lldb/source/Host/windows/MainLoopWindows.cpp6
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp2
-rw-r--r--lldb/source/Interpreter/ScriptInterpreter.cpp13
-rw-r--r--lldb/source/Interpreter/embedded_interpreter.py6
-rw-r--r--lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp13
-rw-r--r--lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp19
-rw-r--r--lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp4
-rw-r--r--lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp9
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp16
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp5
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp2
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp42
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h1
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp4
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp4
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp2
-rw-r--r--lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp2
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp8
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp8
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h2
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp15
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h1
-rw-r--r--lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp12
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp97
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h12
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp65
-rw-r--r--lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h4
-rw-r--r--lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp180
-rw-r--r--lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp244
-rw-r--r--lldb/source/Plugins/Platform/Android/PlatformAndroid.h11
-rw-r--r--lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp2
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp6
-rw-r--r--lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp2
-rw-r--r--lldb/source/Plugins/Process/Utility/LinuxSignals.cpp2
-rw-r--r--lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp44
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp10
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h3
-rw-r--r--lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp2
-rw-r--r--lldb/source/Plugins/Process/wasm/RegisterContextWasm.h3
-rw-r--r--lldb/source/Plugins/Process/wasm/UnwindWasm.cpp22
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp53
-rw-r--r--lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h20
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp26
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h6
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp49
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h27
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h3
-rw-r--r--lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp37
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp19
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp31
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp156
-rw-r--r--lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h20
-rw-r--r--lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp112
-rw-r--r--lldb/source/Protocol/MCP/MCPError.cpp9
-rw-r--r--lldb/source/Protocol/MCP/Server.cpp209
-rw-r--r--lldb/source/Symbol/Function.cpp2
-rw-r--r--lldb/source/Symbol/Symtab.cpp2
-rw-r--r--lldb/source/Target/Language.cpp45
-rw-r--r--lldb/source/Target/RegisterContextUnwind.cpp32
-rw-r--r--lldb/source/Target/Statistics.cpp5
-rw-r--r--lldb/source/Target/StopInfo.cpp71
-rw-r--r--lldb/source/Target/StructuredDataPlugin.cpp2
-rw-r--r--lldb/source/Target/Target.cpp7
-rw-r--r--lldb/source/Target/TargetList.cpp12
-rw-r--r--lldb/source/Target/ThreadPlanStepOut.cpp5
-rw-r--r--lldb/source/Utility/XcodeSDK.cpp10
-rw-r--r--lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py18
-rw-r--r--lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py3
-rw-r--r--lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py3
-rw-r--r--lldb/test/API/commands/protocol/TestMCPUnixSocket.py13
-rw-r--r--lldb/test/API/driver/quit_speed/TestQuitWithProcess.py25
-rw-r--r--lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py1
-rw-r--r--lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile4
-rw-r--r--lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py102
-rw-r--r--lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py49
-rw-r--r--lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c14
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/Makefile3
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/TestDataFormatterInvalidAtomic.py45
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/main.cpp23
-rw-r--r--lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered_map-iterator/TestDataFormatterStdUnorderedMap.py5
-rw-r--r--lldb/test/API/functionalities/json/symbol-file/Makefile1
-rw-r--r--lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py28
-rw-r--r--lldb/test/API/functionalities/stats_api/arm64-minidump-build-ids.yaml19
-rw-r--r--lldb/test/API/functionalities/thread/step_out_line0/Makefile3
-rw-r--r--lldb/test/API/functionalities/thread/step_out_line0/TestThreadStepOutLine0.py35
-rw-r--r--lldb/test/API/functionalities/thread/step_out_line0/main.c11
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py26
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml29
-rw-r--r--lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json6
-rw-r--r--lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py33
-rw-r--r--lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py20
-rw-r--r--lldb/test/API/lang/cpp/floating-types-specialization/Makefile3
-rw-r--r--lldb/test/API/lang/cpp/floating-types-specialization/TestCppFloatingTypesSpecialization.py37
-rw-r--r--lldb/test/API/lang/cpp/floating-types-specialization/main.cpp11
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/Makefile3
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/TestFunctionCallFromObjectFile.py29
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/common.h8
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/lib1.cpp8
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/lib2.cpp6
-rw-r--r--lldb/test/API/lang/cpp/function-call-from-object-file/main.cpp10
-rw-r--r--lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py31
-rw-r--r--lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py9
-rw-r--r--lldb/test/API/lang/objc/ivar-in-framework-base/Makefile6
-rw-r--r--lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py39
-rw-r--r--lldb/test/API/lang/objc/ivar-in-framework-base/lib.h6
-rw-r--r--lldb/test/API/lang/objc/ivar-in-framework-base/lib.m8
-rw-r--r--lldb/test/API/lang/objc/ivar-in-framework-base/main.m22
-rw-r--r--lldb/test/API/macosx/debugserver-multimemread/Makefile3
-rw-r--r--lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py102
-rw-r--r--lldb/test/API/macosx/debugserver-multimemread/main.c14
-rw-r--r--lldb/test/API/macosx/mte/Makefile15
-rw-r--r--lldb/test/API/macosx/mte/TestDarwinMTE.py122
-rw-r--r--lldb/test/API/macosx/mte/main.c28
-rw-r--r--lldb/test/API/macosx/mte/mte-entitlements.plist10
-rw-r--r--lldb/test/API/python_api/debugger/TestDebuggerAPI.py147
-rw-r--r--lldb/test/API/python_api/default-constructor/sb_filespec.py2
-rw-r--r--lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py50
-rw-r--r--lldb/test/API/python_api/sbtype_basic_type/TestSBTypeBasicType.py8
-rw-r--r--lldb/test/API/tools/lldb-dap/attach-commands/Makefile3
-rw-r--r--lldb/test/API/tools/lldb-dap/attach-commands/TestDAP_attachCommands.py145
-rw-r--r--lldb/test/API/tools/lldb-dap/attach-commands/main.c30
-rw-r--r--lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py205
-rw-r--r--lldb/test/API/tools/lldb-dap/attach/main.c11
-rw-r--r--lldb/test/API/tools/lldb-dap/breakpoint-events/TestDAP_breakpointEvents.py4
-rw-r--r--lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/io/TestDAP_io.py10
-rw-r--r--lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py22
-rw-r--r--lldb/test/API/tools/lldb-dap/module-event/TestDAP_module_event.py12
-rw-r--r--lldb/test/API/tools/lldb-dap/module/TestDAP_module.py4
-rw-r--r--lldb/test/API/tools/lldb-dap/output/TestDAP_output.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_console.py2
-rw-r--r--lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py3
-rw-r--r--lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py35
-rw-r--r--lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py1
-rw-r--r--lldb/test/API/tools/lldb-server/TestLldbGdbServer.py4
-rw-r--r--lldb/test/API/tools/lldb-server/main.cpp7
-rw-r--r--lldb/test/Shell/Driver/LocalLLDBInit.test2
-rw-r--r--lldb/test/Shell/Expr/TestExprLanguageNote.test88
-rw-r--r--lldb/test/Shell/Expr/TestGlobalSymbolObjCConflict.c35
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/incomplete-member-beyond-parent-bounds.yaml104
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/member-beyond-parent-bounds.yaml109
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/member-on-parent-bounds.yaml109
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/union-types-no-member-location.yaml4
-rw-r--r--lldb/test/Shell/SymbolFile/DWARF/zero-sized-member-in-parent-bounds.yaml105
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/break-by-function.cpp6
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/break-by-line.cpp2
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/c-calling-conventions.cpp51
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/disassembly.cpp2
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp7
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/local-variables-registers.s4
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp10
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/simple-types.cpp142
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp12
-rw-r--r--lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp2
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/function-nested-block.test2
-rw-r--r--lldb/test/Shell/SymbolFile/PDB/variables.test6
-rwxr-xr-xlldb/test/Shell/helper/build.py8
-rw-r--r--lldb/test/Shell/lit.cfg.py2
-rw-r--r--lldb/tools/debugserver/source/DNB.cpp10
-rw-r--r--lldb/tools/debugserver/source/DNB.h3
-rw-r--r--lldb/tools/debugserver/source/DNBDefs.h3
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.h2
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachTask.mm25
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp59
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMMemory.h2
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp46
-rw-r--r--lldb/tools/debugserver/source/MacOSX/MachVMRegion.h3
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.cpp403
-rw-r--r--lldb/tools/debugserver/source/RNBRemote.h4
-rw-r--r--lldb/tools/driver/CMakeLists.txt4
-rw-r--r--lldb/tools/driver/Driver.cpp52
-rw-r--r--lldb/tools/lldb-dap/DAP.h6
-rw-r--r--lldb/tools/lldb-dap/Handler/RequestHandler.cpp4
-rw-r--r--lldb/tools/lldb-dap/JSONUtils.cpp15
-rw-r--r--lldb/tools/lldb-dap/JSONUtils.h7
-rw-r--r--lldb/tools/lldb-dap/Options.td6
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolBase.h6
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp2
-rw-r--r--lldb/tools/lldb-dap/Protocol/ProtocolRequests.h1
-rw-r--r--lldb/tools/lldb-dap/ProtocolUtils.cpp8
-rw-r--r--lldb/tools/lldb-dap/Transport.h11
-rw-r--r--lldb/tools/lldb-dap/package.json5
-rw-r--r--lldb/tools/lldb-dap/tool/lldb-dap.cpp98
-rw-r--r--lldb/tools/lldb-mcp/lldb-mcp.cpp2
-rw-r--r--lldb/unittests/API/CMakeLists.txt6
-rw-r--r--lldb/unittests/API/SBBreakpointClearConditionTest.cpp69
-rw-r--r--lldb/unittests/Breakpoint/CMakeLists.txt20
-rw-r--r--lldb/unittests/CMakeLists.txt6
-rw-r--r--lldb/unittests/DAP/CMakeLists.txt2
-rw-r--r--lldb/unittests/DAP/DAPTest.cpp20
-rw-r--r--lldb/unittests/DAP/Handler/DisconnectTest.cpp4
-rw-r--r--lldb/unittests/DAP/ProtocolTypesTest.cpp25
-rw-r--r--lldb/unittests/DAP/TestBase.cpp42
-rw-r--r--lldb/unittests/DAP/TestBase.h123
-rw-r--r--lldb/unittests/Host/JSONTransportTest.cpp519
-rw-r--r--lldb/unittests/Host/MainLoopTest.cpp2
-rw-r--r--lldb/unittests/Host/posix/HostTest.cpp19
-rw-r--r--lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp22
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPServerTest.cpp312
-rw-r--r--lldb/unittests/Protocol/ProtocolMCPTest.cpp5
-rw-r--r--lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp10
-rw-r--r--lldb/unittests/Symbol/TestTypeSystemClang.cpp3
-rw-r--r--lldb/unittests/Target/CMakeLists.txt1
-rw-r--r--lldb/unittests/Target/LanguageTest.cpp82
-rw-r--r--lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h100
-rw-r--r--lldb/unittests/Utility/XcodeSDKTest.cpp1
-rw-r--r--lldb/utils/lui/lldbutil.py4
-rwxr-xr-xlldb/utils/lui/lui.py6
-rwxr-xr-xlldb/utils/lui/sandbox.py5
293 files changed, 6957 insertions, 2000 deletions
diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig
index 4721dfdc17e6..3ea24f1a3141 100644
--- a/lldb/bindings/python/python-swigsafecast.swig
+++ b/lldb/bindings/python/python-swigsafecast.swig
@@ -142,5 +142,9 @@ PythonObject SWIGBridge::ToSWIGWrapper(
return ToSWIGHelper(module_spec_sb.release(), SWIGTYPE_p_lldb__SBModuleSpec);
}
+PythonObject SWIGBridge::ToSWIGWrapper(lldb::DescriptionLevel level) {
+ return PythonInteger((int64_t) level);
+}
+
} // namespace python
} // namespace lldb_private
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
index 88b6cd9ef6b6..715914fe745f 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -233,6 +233,11 @@ AND call SWIG_fail at the same time, because it will result in a double free.
}
+// For lldb::SBFileSpec::GetPath
+%typemap(in) (char *dst_path, size_t dst_len) = (char *dst_or_null, size_t dst_len);
+%typemap(argout) (char *dst_path, size_t dst_len) = (char *dst_or_null, size_t dst_len);
+
+
// typemap for an outgoing buffer
// See also SBEvent::SBEvent(uint32_t event, const char *cstr, uint32_t cstr_len).
// Ditto for SBProcess::PutSTDIN(const char *src, size_t src_len).
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 2c30d536a753..64b7dc838107 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -422,6 +422,30 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *
return sb_ptr;
}
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject * data) {
+ lldb::SBFrame *sb_ptr = nullptr;
+
+ int valid_cast =
+ SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBFrame, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject * data) {
+ lldb::SBBreakpointLocation *sb_ptr = nullptr;
+
+ int valid_cast =
+ SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
+
+ if (valid_cast == -1)
+ return NULL;
+
+ return sb_ptr;
+}
+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject * data) {
lldb::SBAttachInfo *sb_ptr = nullptr;
diff --git a/lldb/cmake/modules/AddLLDB.cmake b/lldb/cmake/modules/AddLLDB.cmake
index 28bf8d816d89..5d58abf237f5 100644
--- a/lldb/cmake/modules/AddLLDB.cmake
+++ b/lldb/cmake/modules/AddLLDB.cmake
@@ -167,6 +167,24 @@ function(add_lldb_executable name)
)
target_link_libraries(${name} PRIVATE ${ARG_LINK_LIBS})
+ if(WIN32)
+ list(FIND ARG_LINK_LIBS liblldb LIBLLDB_INDEX)
+ if(NOT LIBLLDB_INDEX EQUAL -1)
+ if (MSVC)
+ target_link_options(${name} PRIVATE "/DELAYLOAD:$<TARGET_FILE_NAME:liblldb>")
+ elseif (MINGW AND LINKER_IS_LLD)
+ # LLD can delay load just by passing a --delayload flag, as long as the import
+ # library is a short type import library (which LLD and MS link.exe produce).
+ # With ld.bfd, with long import libraries (as produced by GNU binutils), one
+ # has to generate a separate delayload import library with dlltool.
+ target_link_options(${name} PRIVATE "-Wl,--delayload=$<TARGET_FILE_NAME:liblldb>")
+ elseif (DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH)
+ # If liblldb can't be delayloaded, then LLDB_PYTHON_DLL_RELATIVE_PATH will not
+ # have any effect.
+ message(WARNING "liblldb is not delay loaded, LLDB_PYTHON_DLL_RELATIVE_PATH has no effect")
+ endif()
+ endif()
+ endif()
if(CLANG_LINK_CLANG_DYLIB)
target_link_libraries(${name} PRIVATE clang-cpp)
else()
diff --git a/lldb/cmake/modules/LLDBFramework.cmake b/lldb/cmake/modules/LLDBFramework.cmake
index c6f00ed05cfc..23d9d49d0cef 100644
--- a/lldb/cmake/modules/LLDBFramework.cmake
+++ b/lldb/cmake/modules/LLDBFramework.cmake
@@ -68,8 +68,6 @@ if(NOT APPLE_EMBEDDED)
)
endif()
-find_program(unifdef_EXECUTABLE unifdef)
-
# Wrap output in a target, so lldb-framework can depend on it.
add_custom_target(liblldb-resource-headers DEPENDS lldb-sbapi-dwarf-enums ${lldb_staged_headers})
set_target_properties(liblldb-resource-headers PROPERTIES FOLDER "LLDB/Resources")
diff --git a/lldb/docs/resources/extensions.rst b/lldb/docs/resources/extensions.rst
index 30bd6d5c6b8d..61fffe7a56d2 100644
--- a/lldb/docs/resources/extensions.rst
+++ b/lldb/docs/resources/extensions.rst
@@ -134,5 +134,5 @@ Objective-C runtime
Clang emits the Objective-C runtime version into the
``DW_TAG_compile_unit`` using the
-``DW_AT_APPLE_major_runtime_version`` attribute. The value 2 stands
+``DW_AT_APPLE_major_runtime_vers`` attribute. The value 2 stands
for Objective-C 2.0.
diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md
index 36b95f1073eb..f0c5e6b04d54 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -1,3 +1,6 @@
+<!-- Packets are listed in alpabetical order, and if in a section, alphabetical
+ order within that section. -->
+
# GDB Remote Protocol Extensions
LLDB has added new GDB server packets to better support multi-threaded and
@@ -735,6 +738,57 @@ This is a performance optimization, which speeds up debugging by avoiding
multiple round-trips for retrieving thread information. The information from this
packet can be retrieved using a combination of `qThreadStopInfo` and `m` packets.
+### MultiMemRead
+
+Read memory from multiple memory ranges.
+
+This packet has one argument:
+
+* `ranges`: a list of pairs of numbers, formatted in base-16. Each pair is
+separated by a `,`, as is each number in each pair. The first number of the
+pair denotes the base address of the memory read, the second denotes the number
+of bytes to be read. The list must end with a `;`.
+
+The reply packet starts with a comma-separated list of numbers formatted in
+base-16, denoting how many bytes were read from each range, in the same order
+as the request packet. The list is followed by a `;`, followed by a sequence of
+bytes containing binary encoded data for all memory that was read. The length
+of the binary encodeed data, after being decoded as required by the GDB remote
+protocol, must be equal to the sum of the numbers provided at the start of the
+reply. The order of the binary data is the same as the order of the ranges in
+the request packet.
+
+If some part of a range is not readable, the stub may perform a partial read of
+a prefix of the range. In other words, partial reads will only ever be from the
+start of the range, never the middle or end. Support for partial reads depends
+on the debug stub.
+
+If, by applying the rules above, the stub has read zero bytes from a range, it
+must return a length of zero for that range in the reply packet; no bytes for
+this memory range are included in the sequence of bytes that follows.
+
+A stub that supports this packet must include `MultiMemRead+` in the reply to
+`qSupported`.
+
+```
+send packet: $MultiMemRead:ranges:100a00,4,200200,a0,400000,4;
+read packet: $4,0,2;<binary encoding of abcd1000><binary encoding of eeff>
+```
+
+In the example above, the first read produced `abcd1000`, the read of `a0`
+bytes from address `200200` failed to read any bytes, and the third read
+produced two bytes – `eeff` – out of the four requested.
+
+```
+send packet: $MultiMemRead:ranges:100a00,0;
+read packet: $0;
+```
+
+In the example above, a read of zero bytes was requested.
+
+**Priority to Implement:** Only required for performance, the debugger will
+fall back to doing separate read requests if this packet is unavailable.
+
## QEnvironment:NAME=VALUE
Setup the environment up for a new child process that will soon be
@@ -2427,43 +2481,6 @@ Response is `F` plus the return value of `unlink()`, base 16 encoding.
Return value may optionally be followed by a comma and the base16
value of errno if unlink failed.
-## "x" - Binary memory read
-
-> **Warning:** The format of this packet was decided before GDB 16
-> introduced its own format for `x`. Future versions of LLDB may not
-> support the format described here, and new code should produce and
-> expect the format used by GDB.
-
-Like the `m` (read) and `M` (write) packets, this is a partner to the
-`X` (write binary data) packet, `x`.
-
-It is called like
-```
-xADDRESS,LENGTH
-```
-
-where both `ADDRESS` and `LENGTH` are big-endian base 16 values.
-
-To test if this packet is available, send a addr/len of 0:
-```
-x0,0
-```
-You will get an `OK` response if it is supported.
-
-The reply will be the data requested in 8-bit binary data format.
-The standard quoting is applied to the payload. Characters `} # $ *`
-will all be escaped with `}` (`0x7d`) character and then XOR'ed with `0x20`.
-
-A typical use to read 512 bytes at 0x1000 would look like:
-```
-x0x1000,0x200
-```
-The `0x` prefixes are optional - like most of the gdb-remote packets,
-omitting them will work fine; these numbers are always base 16.
-
-The length of the payload is not provided. A reliable, 8-bit clean,
-transport layer is assumed.
-
## Wasm Packets
The packet below are supported by the
@@ -2530,3 +2547,40 @@ read packet: $e0030100#b9
**Priority to Implement:** Only required for Wasm support. Necessary to show
variables.
+
+## "x" - Binary memory read
+
+> **Warning:** The format of this packet was decided before GDB 16
+> introduced its own format for `x`. Future versions of LLDB may not
+> support the format described here, and new code should produce and
+> expect the format used by GDB.
+
+Like the `m` (read) and `M` (write) packets, this is a partner to the
+`X` (write binary data) packet, `x`.
+
+It is called like
+```
+xADDRESS,LENGTH
+```
+
+where both `ADDRESS` and `LENGTH` are big-endian base 16 values.
+
+To test if this packet is available, send a addr/len of 0:
+```
+x0,0
+```
+You will get an `OK` response if it is supported.
+
+The reply will be the data requested in 8-bit binary data format.
+The standard quoting is applied to the payload. Characters `} # $ *`
+will all be escaped with `}` (`0x7d`) character and then XOR'ed with `0x20`.
+
+A typical use to read 512 bytes at 0x1000 would look like:
+```
+x0x1000,0x200
+```
+The `0x` prefixes are optional - like most of the gdb-remote packets,
+omitting them will work fine; these numbers are always base 16.
+
+The length of the payload is not provided. A reliable, 8-bit clean,
+transport layer is assumed.
diff --git a/lldb/docs/use/python-reference.rst b/lldb/docs/use/python-reference.rst
index 6ac2ec93fbd1..afca07520d8a 100644
--- a/lldb/docs/use/python-reference.rst
+++ b/lldb/docs/use/python-reference.rst
@@ -27,4 +27,4 @@ The following tutorials and documentation demonstrate various Python capabilitie
tutorials/writing-custom-commands
tutorials/implementing-standalone-scripts
tutorials/custom-frame-recognizers
- tutorials/extending-target-stop-hooks \ No newline at end of file
+ tutorials/extending-target-stop-hooks
diff --git a/lldb/docs/use/tutorials/creating-custom-breakpoints.md b/lldb/docs/use/tutorials/creating-custom-breakpoints.md
index e3081c44e365..04673c310ccf 100644
--- a/lldb/docs/use/tutorials/creating-custom-breakpoints.md
+++ b/lldb/docs/use/tutorials/creating-custom-breakpoints.md
@@ -125,4 +125,48 @@ you can use for this purpose. Your __init__ function gets passed this
SBStructuredData object. This API also allows you to directly provide the list
of Modules and the list of CompileUnits that will make up the SearchFilter. If
you pass in empty lists, the breakpoint will use the default "search
-everywhere,accept everything" filter. \ No newline at end of file
+everywhere,accept everything" filter.
+
+### Providing Facade Locations:
+
+The breakpoint resolver interface also allows you to present a separate set
+of locations for the breakpoint than the ones that actually implement the
+breakpoint in the target.
+
+An example use case for this is if you are providing a debugging interface for a
+library that implements an interpreter for a language lldb can't debug. But
+while debugging that library at the level of the implementation language (e.g. C/C++, etc)
+you would like to offer the ability to "stop when a line in a source language
+file is executed".
+
+You can do this if you know where new lines of code are dispatched in the
+interpreter. You would set a breakpoint there, and then look at the state
+when that breakpoint is hit to see if it is dispatching the source file and
+line that were requested, and stop appropriately.
+
+Facade breakpoint locations are intended to make a more natural presentation
+of that sort of feature. The idea is that you would make a custom breakpoint
+resolver that sets actual locations in the places of interest in the interpreter.
+
+Then your resolver would add "facade locations" that represent the places in the
+interpreted code that you want the breakpoint to stop at, using SBBreakpoint::AddFacadeLocation.
+When lldb describes the breakpoint, it will only show the Facade locations.
+Since facade breakpoint location's description is customizable, you can make these
+locations more descriptive. And when the "real" location is hit, lldb will call the
+"was_hit" method of your resolver. That will return the facade location you
+consider to have been hit this time around, or if you return None, the breakpoint
+will be considered not to have been hit.
+
+Note, this feature is also useful if you don't intend to present facade
+locations since it essentially provides a scripted breakpoint condition. Every
+time one of the locations in your breakpoint is hit, you can run the code in
+your "was_hit" to determine whether to consider the breakpoint hit or not, and
+return the location you were passed in if you want it to be a hit, and None if not.
+
+The Facade location adds these optional affordances to the Resolver class:
+
+| Name | Arguments | Description|
+|-------|-----------|------------|
+|`was_hit`| `frame`:`lldb.SBFrame` `bp_loc`:`lldb.SBBreakpointLocation` | This will get called when one of the "real" locations set by your resolver is hit. `frame` is the stack frame that hit this location. `bp_loc` is the real location that was hit. Return either the facade location that you want to consider hit on this stop, or None if you don't consider any of your facade locations to have been hit. |
+| `get_location_description` | `bp_loc`:`lldb.SBBreakpointLocation` `desc_level`:`lldb.DescriptionLevel` `bp_loc` is the facade location to describe.| Use this to provide a helpful description of each facade location. ``desc_level`` is the level of description requested. The Brief description is printed when the location is hit. Full is printed for `break list` and Verbose for `break list -v`.|
+
diff --git a/lldb/examples/python/lldbtk.py b/lldb/examples/python/lldbtk.py
index f1685c04313a..72fe91e4e932 100644
--- a/lldb/examples/python/lldbtk.py
+++ b/lldb/examples/python/lldbtk.py
@@ -4,12 +4,8 @@ import lldb
import shlex
import sys
-try:
- from tkinter import *
- import tkinter.ttk as ttk
-except ImportError:
- from Tkinter import *
- import ttk
+from tkinter import *
+import tkinter.ttk as ttk
class ValueTreeItemDelegate(object):
diff --git a/lldb/examples/python/performance.py b/lldb/examples/python/performance.py
index b86b5a52522e..c3181b61c84f 100755
--- a/lldb/examples/python/performance.py
+++ b/lldb/examples/python/performance.py
@@ -16,7 +16,6 @@ import resource
import sys
import subprocess
import time
-import types
# ----------------------------------------------------------------------
# Code that auto imports LLDB
@@ -121,19 +120,19 @@ class BreakpointAction(Action):
self.breakpoints.append(breakpoint)
else:
if module:
- if isinstance(module, types.ListType):
+ if isinstance(module, list):
for module_path in module:
self.modules.Append(lldb.SBFileSpec(module_path, False))
- elif isinstance(module, types.StringTypes):
+ elif isinstance(module, str):
self.modules.Append(lldb.SBFileSpec(module, False))
if name:
# "file" can be a list or a string
if file:
- if isinstance(file, types.ListType):
+ if isinstance(file, list):
self.files = lldb.SBFileSpecList()
for f in file:
self.files.Append(lldb.SBFileSpec(f, False))
- elif isinstance(file, types.StringTypes):
+ elif isinstance(file, str):
self.files.Append(lldb.SBFileSpec(file, False))
self.breakpoints.append(
self.target.BreakpointCreateByName(name, self.modules, self.files)
diff --git a/lldb/examples/summaries/cocoa/CFString.py b/lldb/examples/summaries/cocoa/CFString.py
index 74bd927e9db2..02b670651cd5 100644
--- a/lldb/examples/summaries/cocoa/CFString.py
+++ b/lldb/examples/summaries/cocoa/CFString.py
@@ -11,11 +11,6 @@ import lldb
import lldb.runtime.objc.objc_runtime
import lldb.formatters.Logger
-try:
- unichr
-except NameError:
- unichr = chr
-
def CFString_SummaryProvider(valobj, dict):
logger = lldb.formatters.Logger.Logger()
@@ -107,7 +102,7 @@ class CFStringSynthProvider:
value = b1 * 256 + b0
else:
value = b0 * 256 + b1
- pystr = pystr + unichr(value)
+ pystr = pystr + chr(value)
# read max_len unicode values, not max_len bytes
max_len = max_len - 1
return pystr
diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h
index 18ed3e7226d3..fe19ba998ea6 100644
--- a/lldb/include/lldb/API/SBBreakpoint.h
+++ b/lldb/include/lldb/API/SBBreakpoint.h
@@ -153,9 +153,15 @@ public:
/// fails, e.g. when there aren't enough hardware resources available.
lldb::SBError SetIsHardware(bool is_hardware);
- // Can only be called from a ScriptedBreakpointResolver...
+ /// Adds a location to the breakpoint at the address passed in.
+ /// Can only be called from a ScriptedBreakpointResolver...
SBError
AddLocation(SBAddress &address);
+ /// Add a "Facade location" to the breakpoint. This returns the Facade
+ /// Location that was added, which you can then use in
+ /// get_location_description and was_hit in your breakpoint resolver.
+ /// Can only be called from a ScriptedBreakpointResolver.
+ SBBreakpointLocation AddFacadeLocation();
SBStructuredData SerializeToStructuredData();
diff --git a/lldb/include/lldb/API/SBBreakpointLocation.h b/lldb/include/lldb/API/SBBreakpointLocation.h
index fa823e2b518a..9b0d4839aca8 100644
--- a/lldb/include/lldb/API/SBBreakpointLocation.h
+++ b/lldb/include/lldb/API/SBBreakpointLocation.h
@@ -24,6 +24,8 @@ class SWIGBridge;
namespace lldb {
class LLDB_API SBBreakpointLocation {
+ friend class lldb_private::ScriptInterpreter;
+
public:
SBBreakpointLocation();
diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index f77b0c1d7f0e..7a08a0826220 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -359,6 +359,9 @@ public:
lldb::SBTarget FindTargetWithFileAndArch(const char *filename,
const char *arch);
+ /// Find a target with the specified unique ID.
+ lldb::SBTarget FindTargetByGloballyUniqueID(lldb::user_id_t id);
+
/// Get the number of targets in the debugger.
uint32_t GetNumTargets();
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h
index e4bbcd5ddcd9..92917e57fc12 100644
--- a/lldb/include/lldb/API/SBFrame.h
+++ b/lldb/include/lldb/API/SBFrame.h
@@ -226,6 +226,7 @@ protected:
friend class SBThread;
friend class SBValue;
+ friend class lldb_private::ScriptInterpreter;
friend class lldb_private::python::SWIGBridge;
friend class lldb_private::lua::SWIGBridge;
diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h
index 62cdd342a05e..173fd05b54a1 100644
--- a/lldb/include/lldb/API/SBTarget.h
+++ b/lldb/include/lldb/API/SBTarget.h
@@ -357,6 +357,14 @@ public:
const char *GetLabel() const;
+ /// Get the globally unique ID for this target. This ID is unique
+ /// across all debugger instances within the same lldb process.
+ ///
+ /// \return
+ /// The globally unique ID for this target, or
+ /// LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID if the target is invalid.
+ lldb::user_id_t GetGloballyUniqueID() const;
+
SBError SetLabel(const char *label);
/// Architecture opcode byte size width accessor
diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h
index 26a5e901a0d7..c0bad98c95b2 100644
--- a/lldb/include/lldb/Breakpoint/Breakpoint.h
+++ b/lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -248,6 +248,23 @@ public:
/// Returns a pointer to the new location.
lldb::BreakpointLocationSP AddLocation(const Address &addr,
bool *new_location = nullptr);
+ /// Add a `facade` location to the breakpoint's collection of facade
+ /// locations. This is only meant to be called by the breakpoint's resolver.
+ /// Facade locations are placeholders that a scripted breakpoint can use to
+ /// represent the stop locations provided by the breakpoint. The scripted
+ /// breakpoint should record the id of the facade location, and provide
+ /// the description of the location in the GetDescription method
+ /// To emulate hitting a facade location, the breakpoint's WasHit should
+ /// return the ID of the facade that was "hit".
+ ///
+ /// \param[out] new_location
+ /// Set to \b true if a new location was created, to \b false if there
+ /// already was a location at this Address.
+ /// \return
+ /// Returns a pointer to the new location.
+ lldb::BreakpointLocationSP AddFacadeLocation();
+
+ lldb::BreakpointLocationSP GetFacadeLocationByID(lldb::break_id_t);
/// Find a breakpoint location by Address.
///
@@ -268,27 +285,38 @@ public:
/// there is no breakpoint location at that address.
lldb::break_id_t FindLocationIDByAddress(const Address &addr);
- /// Find a breakpoint location for a given breakpoint location ID.
+ /// Find a breakpoint location for a given breakpoint location ID. If there
+ /// are Facade Locations in the breakpoint, the facade locations will be
+ /// searched instead of the "real" ones.
///
/// \param[in] bp_loc_id
/// The ID specifying the location.
+ ///
+ /// \param[in] use_facade
+ /// If \b true, then prefer facade locations over "real" ones if they exist.
+ ///
/// \return
/// Returns a shared pointer to the location with ID \a bp_loc_id. The
/// pointer
/// in the shared pointer will be nullptr if there is no location with that
/// ID.
- lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id);
+ lldb::BreakpointLocationSP FindLocationByID(lldb::break_id_t bp_loc_id,
+ bool use_facade = true);
/// Get breakpoint locations by index.
///
/// \param[in] index
/// The location index.
///
+ /// \param[in] use_facade
+ /// If \b true, then prefer facade locations over "real" ones if they exist.
+ ///
/// \return
/// Returns a shared pointer to the location with index \a
/// index. The shared pointer might contain nullptr if \a index is
/// greater than then number of actual locations.
- lldb::BreakpointLocationSP GetLocationAtIndex(size_t index);
+ lldb::BreakpointLocationSP GetLocationAtIndex(size_t index,
+ bool use_facade = true);
/// Removes all invalid breakpoint locations.
///
@@ -409,9 +437,12 @@ public:
/// Return the number of breakpoint locations that have resolved to actual
/// breakpoint sites.
///
+ /// \param[in] use_facade
+ /// If \b true, then prefer facade locations over "real" ones if they exist.
+ ///
/// \return
/// The number locations resolved breakpoint sites.
- size_t GetNumResolvedLocations() const;
+ size_t GetNumResolvedLocations(bool use_facade = true) const;
/// Return whether this breakpoint has any resolved locations.
///
@@ -421,9 +452,12 @@ public:
/// Return the number of breakpoint locations.
///
+ /// \param[in] use_facade
+ /// If \b true, then prefer facade locations over "real" ones if they exist.
+ ///
/// \return
/// The number breakpoint locations.
- size_t GetNumLocations() const;
+ size_t GetNumLocations(bool use_facade = true) const;
/// Put a description of this breakpoint into the stream \a s.
///
@@ -529,6 +563,19 @@ private:
m_name_list.erase(name_to_remove);
}
+ /// This controls whether to display information about
+ /// the facade locations or the real locations.
+ enum DisplayType {
+ eDisplayFacade = 1, // Display facade locations
+ eDisplayReal = 1 << 1, // Display real locations
+ eDisplayHeader = 1 << 2 // Display compressed list of locations only
+ };
+
+ void GetDescriptionForType(Stream *s, lldb::DescriptionLevel level,
+ uint8_t display_type, bool show_locations);
+
+ bool HasFacadeLocations() { return m_facade_locations.GetSize() != 0; }
+
public:
bool MatchesName(const char *name) {
return m_name_list.find(name) != m_name_list.end();
@@ -657,6 +704,8 @@ private:
BreakpointOptions m_options; // Settable breakpoint options
BreakpointLocationList
m_locations; // The list of locations currently found for this breakpoint.
+ BreakpointLocationCollection m_facade_locations;
+
std::string m_kind_description;
bool m_resolve_indirect_symbols;
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
index ab2e5e170559..c44ff471b8c4 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -38,6 +38,12 @@ namespace lldb_private {
class BreakpointLocation
: public std::enable_shared_from_this<BreakpointLocation> {
+ friend class BreakpointSite;
+ friend class BreakpointLocationList;
+ friend class Breakpoint;
+ friend class Process;
+ friend class StopInfoBreakpoint;
+
public:
~BreakpointLocation();
@@ -55,16 +61,39 @@ public:
Target &GetTarget();
+ /// This is a programmatic version of a breakpoint "condition". When a
+ /// breakpoint is hit, WasHit will get called before the synchronous
+ /// ShouldStop callback is run, and if it returns an empty
+ /// BreakpointLocationSP, lldb will act as if that breakpoint wasn't hit.
+ ///
+ /// \param[in] context
+ /// The context at the stop point
+ ///
+ /// \return
+ /// This will return the breakpoint location that was hit on this stop.
+ /// If there was no facade location this will be the original location.
+ /// If the shared pointer is empty, then we'll treat it as if the
+ /// breakpoint was not hit.
+ lldb::BreakpointLocationSP WasHit(StoppointCallbackContext *context);
+
/// Determines whether we should stop due to a hit at this breakpoint
/// location.
///
/// Side Effects: This may evaluate the breakpoint condition, and run the
/// callback. So this command may do a considerable amount of work.
///
+ /// \param[in] context
+ /// The context at the stop point
+ ///
+ /// \param[out] facade_loc_sp
+ /// If this stop should be attributed not to the location that was hit, but
+ /// to a facade location, it will be returned in this facade_loc_sp.
+ ///
/// \return
/// \b true if this breakpoint location thinks we should stop,
/// \b false otherwise.
- bool ShouldStop(StoppointCallbackContext *context);
+ bool ShouldStop(StoppointCallbackContext *context,
+ lldb::BreakpointLocationSP &facade_loc_sp);
// The next section deals with various breakpoint options.
@@ -292,11 +321,6 @@ public:
}
protected:
- friend class BreakpointSite;
- friend class BreakpointLocationList;
- friend class Process;
- friend class StopInfoBreakpoint;
-
/// Set the breakpoint site for this location to \a bp_site_sp.
///
/// \param[in] bp_site_sp
@@ -346,9 +370,11 @@ private:
// Constructors and Destructors
//
// Only the Breakpoint can make breakpoint locations, and it owns them.
-
/// Constructor.
///
+ /// \param[in] loc_id
+ /// The location id of the new location.
+ ///
/// \param[in] owner
/// A back pointer to the breakpoint that owns this location.
///
@@ -359,37 +385,65 @@ private:
/// The thread for which this breakpoint location is valid, or
/// LLDB_INVALID_THREAD_ID if it is valid for all threads.
///
- BreakpointLocation(lldb::break_id_t bid, Breakpoint &owner,
+ BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner,
const Address &addr, lldb::tid_t tid,
bool check_for_resolver = true);
+ /// This is the constructor for locations with no address. Currently this is
+ /// just used for Facade locations.
+ ///
+ /// \param[in] loc_id
+ /// The location id of the new location.
+ ///
+ /// \param[in] owner
+ /// A back pointer to the breakpoint that owns this location.
+ ///
+ ///
+public:
+ BreakpointLocation(lldb::break_id_t loc_id, Breakpoint &owner);
+ bool IsValid() const { return m_is_valid; }
+ bool IsFacade() const { return m_is_facade; }
+
+private:
// Data members:
bool m_should_resolve_indirect_functions;
bool m_is_reexported;
bool m_is_indirect;
- Address m_address; ///< The address defining this location.
- Breakpoint &m_owner; ///< The breakpoint that produced this object.
- std::unique_ptr<BreakpointOptions> m_options_up; ///< Breakpoint options
- /// pointer, nullptr if we're
- /// using our breakpoint's
- /// options.
- lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be
- ///shared by more than one location.)
- lldb::UserExpressionSP m_user_expression_sp; ///< The compiled expression to
- ///use in testing our condition.
- std::mutex m_condition_mutex; ///< Guards parsing and evaluation of the
- ///condition, which could be evaluated by
- /// multiple processes.
- size_t m_condition_hash; ///< For testing whether the condition source code
- ///changed.
- lldb::break_id_t m_loc_id; ///< Breakpoint location ID.
- StoppointHitCounter m_hit_counter; ///< Number of times this breakpoint
- /// location has been hit.
+ ///< The address defining this location.
+ Address m_address;
+ ///< The breakpoint that produced this object.
+ Breakpoint &m_owner;
+ ///< Breakpoint options pointer, nullptr if we're using our breakpoint's
+ /// options.
+ std::unique_ptr<BreakpointOptions> m_options_up;
+ ///< Our breakpoint site (it may be shared by more than one location.)
+ lldb::BreakpointSiteSP m_bp_site_sp;
+ ///< The compiled expression to use in testing our condition.
+ lldb::UserExpressionSP m_user_expression_sp;
+ ///< Guards parsing and evaluation of the condition, which could be evaluated
+ /// by multiple processes.
+ std::mutex m_condition_mutex;
+ ///< For testing whether the condition source code changed.
+ size_t m_condition_hash;
+ ///< Breakpoint location ID.
+ lldb::break_id_t m_loc_id;
+ ///< Number of times this breakpoint location has been hit.
+ StoppointHitCounter m_hit_counter;
/// If this exists, use it to print the stop description rather than the
/// LineEntry m_address resolves to directly. Use this for instance when the
/// location was given somewhere in the virtual inlined call stack since the
/// Address always resolves to the lowest entry in the stack.
std::optional<LineEntry> m_preferred_line_entry;
+ /// Because Facade locations don't have sites we can't use the presence of
+ /// the site to mean this breakpoint is valid, but must manage the state
+ /// directly.
+ bool m_is_valid = true;
+ /// Facade locations aren't directly triggered and don't have a breakpoint
+ /// site. They are a useful fiction when you want to represent the stop
+ /// location as something lldb can't naturally stop at.
+ bool m_is_facade = false;
+
+ void SetInvalid() { m_is_valid = false; }
void SetShouldResolveIndirectFunctions(bool do_resolve) {
m_should_resolve_indirect_functions = do_resolve;
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h
index 3aef1d658c0e..1df4e074680f 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocationCollection.h
@@ -111,7 +111,8 @@ public:
///
/// \return
/// \b true if we should stop, \b false otherwise.
- bool ShouldStop(StoppointCallbackContext *context);
+ bool ShouldStop(StoppointCallbackContext *context,
+ BreakpointLocationCollection &stopped_bp_locs);
/// Print a description of the breakpoint locations in this list
/// to the stream \a s.
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocationList.h b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h
index 17dc0bfe0314..952db559d6cc 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocationList.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocationList.h
@@ -140,7 +140,8 @@ public:
///
/// \return
/// \b true if we should stop, \b false otherwise.
- bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID);
+ bool ShouldStop(StoppointCallbackContext *context, lldb::break_id_t breakID,
+ lldb::BreakpointLocationSP &bp_loc_sp);
/// Returns the number of elements in this breakpoint location list.
///
diff --git a/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h b/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h
index 0322fd9f46ed..c3c1c80f4904 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointResolverScripted.h
@@ -45,6 +45,13 @@ public:
void GetDescription(Stream *s) override;
+ lldb::BreakpointLocationSP WasHit(lldb::StackFrameSP frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp);
+
+ std::optional<std::string>
+ GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
+ lldb::DescriptionLevel level);
+
void Dump(Stream *s) const override;
/// Methods for support type inquiry through isa, cast, and dyn_cast:
diff --git a/lldb/include/lldb/Breakpoint/BreakpointSite.h b/lldb/include/lldb/Breakpoint/BreakpointSite.h
index 7b3f7be23639..a935b2441c02 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointSite.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointSite.h
@@ -99,7 +99,8 @@ public:
///
/// \return
/// \b true if we should stop, \b false otherwise.
- bool ShouldStop(StoppointCallbackContext *context) override;
+ bool ShouldStop(StoppointCallbackContext *context,
+ BreakpointLocationCollection &stopping_bp_loc) override;
/// Standard Dump method
void Dump(Stream *s) const override;
diff --git a/lldb/include/lldb/Breakpoint/StopPointSiteList.h b/lldb/include/lldb/Breakpoint/StopPointSiteList.h
index 7ed53e952dc8..101eccda4616 100644
--- a/lldb/include/lldb/Breakpoint/StopPointSiteList.h
+++ b/lldb/include/lldb/Breakpoint/StopPointSiteList.h
@@ -213,30 +213,6 @@ public:
typedef void (*StopPointSiteSPMapFunc)(StopPointSite &site, void *baton);
- /// Enquires of the site on in this list with ID \a site_id
- /// whether we should stop for the constituent or not.
- ///
- /// \param[in] context
- /// This contains the information about this stop.
- ///
- /// \param[in] site_id
- /// This site ID that we hit.
- ///
- /// \return
- /// \b true if we should stop, \b false otherwise.
- bool ShouldStop(StoppointCallbackContext *context,
- typename StopPointSite::SiteID site_id) {
- if (StopPointSiteSP site_sp = FindByID(site_id)) {
- // Let the site decide if it should stop here (could not have
- // reached it's target hit count yet, or it could have a callback that
- // decided it shouldn't stop (shared library loads/unloads).
- return site_sp->ShouldStop(context);
- }
- // We should stop here since this site isn't valid anymore or it
- // doesn't exist.
- return true;
- }
-
/// Returns the number of elements in the list.
///
/// \result
diff --git a/lldb/include/lldb/Breakpoint/StoppointSite.h b/lldb/include/lldb/Breakpoint/StoppointSite.h
index bef19f37908c..2ceac403940e 100644
--- a/lldb/include/lldb/Breakpoint/StoppointSite.h
+++ b/lldb/include/lldb/Breakpoint/StoppointSite.h
@@ -38,7 +38,12 @@ public:
virtual bool IsHardware() const = 0;
- virtual bool ShouldStop(StoppointCallbackContext* context) = 0;
+ virtual bool ShouldStop(StoppointCallbackContext *context) { return false; };
+
+ virtual bool ShouldStop(StoppointCallbackContext *context,
+ BreakpointLocationCollection &stopping_bp_locs) {
+ return false;
+ };
virtual void Dump(Stream* stream) const = 0;
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 06136ed40471..ead2ed35fadd 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -682,6 +682,7 @@ protected:
lldb::LockableStreamFileSP GetErrorStreamSP() { return m_error_stream_sp; }
/// @}
+ bool IsEscapeCodeCapableTTY();
bool StatuslineSupported();
void PushIOHandler(const lldb::IOHandlerSP &reader_sp,
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 47f1c6a8d80b..546d7a9b409e 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -148,13 +148,7 @@ public:
/// Mangled name get accessor.
///
/// \return
- /// A reference to the mangled name string object.
- ConstString &GetMangledName() { return m_mangled; }
-
- /// Mangled name get accessor.
- ///
- /// \return
- /// A const reference to the mangled name string object.
+ /// The mangled name string object.
ConstString GetMangledName() const { return m_mangled; }
/// Best name get accessor.
@@ -251,7 +245,7 @@ public:
/// \return
/// eManglingSchemeNone if no known mangling scheme could be identified
/// for s, otherwise the enumerator for the mangling scheme detected.
- static Mangled::ManglingScheme GetManglingScheme(llvm::StringRef const name);
+ static Mangled::ManglingScheme GetManglingScheme(llvm::StringRef name);
static bool IsMangledName(llvm::StringRef name);
diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h
index 6ecdcf10fa85..e71f3b2bad6b 100644
--- a/lldb/include/lldb/Core/ModuleList.h
+++ b/lldb/include/lldb/Core/ModuleList.h
@@ -435,7 +435,7 @@ public:
size_t Remove(ModuleList &module_list);
- bool RemoveIfOrphaned(const Module *module_ptr);
+ bool RemoveIfOrphaned(const lldb::ModuleWP module_ptr);
size_t RemoveOrphans(bool mandatory);
@@ -489,7 +489,7 @@ public:
static size_t RemoveOrphanSharedModules(bool mandatory);
- static bool RemoveSharedModuleIfOrphaned(const Module *module_ptr);
+ static bool RemoveSharedModuleIfOrphaned(const lldb::ModuleWP module_ptr);
/// Applies 'callback' to each module in this ModuleList.
/// If 'callback' returns false, iteration terminates.
@@ -531,6 +531,9 @@ protected:
Notifier *m_notifier = nullptr;
+ /// An orphaned module that lives only in the ModuleList has a count of 1.
+ static constexpr long kUseCountModuleListOrphaned = 1;
+
public:
typedef LockingAdaptedIterable<std::recursive_mutex, collection>
ModuleIterable;
diff --git a/lldb/include/lldb/Host/JSONTransport.h b/lldb/include/lldb/Host/JSONTransport.h
index c73021d20425..892821ba3695 100644
--- a/lldb/include/lldb/Host/JSONTransport.h
+++ b/lldb/include/lldb/Host/JSONTransport.h
@@ -18,6 +18,7 @@
#include "lldb/Utility/IOObject.h"
#include "lldb/Utility/Status.h"
#include "lldb/lldb-forward.h"
+#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@@ -25,13 +26,24 @@
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/raw_ostream.h"
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <optional>
#include <string>
#include <system_error>
+#include <type_traits>
+#include <utility>
#include <variant>
#include <vector>
+#if __cplusplus >= 202002L
+#include <concepts>
+#endif
-namespace lldb_private {
+namespace lldb_private::transport {
+/// An error to indicate that the transport reached EOF but there were still
+/// unhandled contents in the read buffer.
class TransportUnhandledContentsError
: public llvm::ErrorInfo<TransportUnhandledContentsError> {
public:
@@ -50,17 +62,75 @@ private:
std::string m_unhandled_contents;
};
+/// An error to indicate that the parameters of a Req, Resp or Evt could not be
+/// deserialized.
+class InvalidParams : public llvm::ErrorInfo<InvalidParams> {
+public:
+ static char ID;
+
+ explicit InvalidParams(std::string method, std::string context)
+ : m_method(std::move(method)), m_context(std::move(context)) {}
+
+ void log(llvm::raw_ostream &OS) const override;
+ std::error_code convertToErrorCode() const override;
+
+private:
+ /// The JSONRPC remote method call.
+ std::string m_method;
+
+ /// Additional context from the parsing failure, e.g. "missing value at
+ /// (root)[1].str".
+ std::string m_context;
+};
+
+/// An error to indicate that no handler was registered for a given method.
+class MethodNotFound : public llvm::ErrorInfo<MethodNotFound> {
+public:
+ static char ID;
+
+ static constexpr int kErrorCode = -32601;
+
+ explicit MethodNotFound(std::string method) : m_method(std::move(method)) {}
+
+ void log(llvm::raw_ostream &OS) const override;
+ std::error_code convertToErrorCode() const override;
+
+private:
+ std::string m_method;
+};
+
+#if __cplusplus >= 202002L
+/// A ProtocolDescriptor details the types used in a JSONTransport for handling
+/// transport communication.
+template <typename T>
+concept ProtocolDescriptor = requires {
+ typename T::Id;
+ typename T::Req;
+ typename T::Resp;
+ typename T::Evt;
+};
+#endif
+
/// 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:
+/// JSONTransport 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 {
+///
+#if __cplusplus >= 202002L
+template <ProtocolDescriptor Proto>
+#else
+template <typename Proto>
+#endif
+class JSONTransport {
public:
+ using Req = typename Proto::Req;
+ using Resp = typename Proto::Resp;
+ using Evt = typename Proto::Evt;
using Message = std::variant<Req, Resp, Evt>;
- virtual ~Transport() = default;
+ virtual ~JSONTransport() = default;
/// Sends an event, a message that does not require a response.
virtual llvm::Error Send(const Evt &) = 0;
@@ -69,7 +139,8 @@ public:
/// Sends a response to a specific request.
virtual llvm::Error Send(const Resp &) = 0;
- /// Implemented to handle incoming messages. (See Run() below).
+ /// Implemented to handle incoming messages. (See `RegisterMessageHandler()`
+ /// below).
class MessageHandler {
public:
virtual ~MessageHandler() = default;
@@ -90,8 +161,6 @@ public:
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.
///
@@ -108,18 +177,23 @@ protected:
};
/// An IOTransport sends and receives messages using an IOObject.
-template <typename Req, typename Resp, typename Evt>
-class IOTransport : public Transport<Req, Resp, Evt> {
+template <typename Proto> class IOTransport : public JSONTransport<Proto> {
public:
- using Transport<Req, Resp, Evt>::Transport;
- using MessageHandler = typename Transport<Req, Resp, Evt>::MessageHandler;
+ using Message = typename JSONTransport<Proto>::Message;
+ using MessageHandler = typename JSONTransport<Proto>::MessageHandler;
IOTransport(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::Error Send(const typename Proto::Evt &evt) override {
+ return Write(evt);
+ }
+ llvm::Error Send(const typename Proto::Req &req) override {
+ return Write(req);
+ }
+ llvm::Error Send(const typename Proto::Resp &resp) override {
+ return Write(resp);
+ }
llvm::Expected<MainLoop::ReadHandleUP>
RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) override {
@@ -139,7 +213,7 @@ public:
/// detail.
static constexpr size_t kReadBufferSize = 1024;
- // FIXME: Write should be protected.
+protected:
llvm::Error Write(const llvm::json::Value &message) {
this->Logv("<-- {0}", message);
std::string output = Encode(message);
@@ -147,7 +221,6 @@ public:
return m_out->Write(output.data(), bytes_written).takeError();
}
-protected:
virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
virtual std::string Encode(const llvm::json::Value &message) = 0;
@@ -174,9 +247,8 @@ private:
}
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);
+ llvm::Expected<Message> message =
+ llvm::json::parse<Message>(raw_message);
if (!message) {
handler.OnError(message.takeError());
return;
@@ -201,10 +273,14 @@ private:
};
/// A transport class for JSON with a HTTP header.
-template <typename Req, typename Resp, typename Evt>
-class HTTPDelimitedJSONTransport : public IOTransport<Req, Resp, Evt> {
+#if __cplusplus >= 202002L
+template <ProtocolDescriptor Proto>
+#else
+template <typename Proto>
+#endif
+class HTTPDelimitedJSONTransport : public IOTransport<Proto> {
public:
- using IOTransport<Req, Resp, Evt>::IOTransport;
+ using IOTransport<Proto>::IOTransport;
protected:
/// Encodes messages based on
@@ -230,8 +306,8 @@ protected:
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.
+ // 'Content-Length' is the only meaningful key at the moment. Others
+ // are ignored.
if (!key.equals_insensitive(kHeaderContentLength))
continue;
@@ -268,10 +344,14 @@ protected:
};
/// A transport class for JSON RPC.
-template <typename Req, typename Resp, typename Evt>
-class JSONRPCTransport : public IOTransport<Req, Resp, Evt> {
+#if __cplusplus >= 202002L
+template <ProtocolDescriptor Proto>
+#else
+template <typename Proto>
+#endif
+class JSONRPCTransport : public IOTransport<Proto> {
public:
- using IOTransport<Req, Resp, Evt>::IOTransport;
+ using IOTransport<Proto>::IOTransport;
protected:
std::string Encode(const llvm::json::Value &message) override {
@@ -297,6 +377,497 @@ protected:
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
};
-} // namespace lldb_private
+/// A handler for the response to an outgoing request.
+template <typename T>
+using Reply =
+ std::conditional_t<std::is_void_v<T>,
+ llvm::unique_function<void(llvm::Error)>,
+ llvm::unique_function<void(llvm::Expected<T>)>>;
+
+namespace detail {
+template <typename R, typename P> struct request_t final {
+ using type = llvm::unique_function<void(const P &, Reply<R>)>;
+};
+template <typename R> struct request_t<R, void> final {
+ using type = llvm::unique_function<void(Reply<R>)>;
+};
+template <typename P> struct event_t final {
+ using type = llvm::unique_function<void(const P &)>;
+};
+template <> struct event_t<void> final {
+ using type = llvm::unique_function<void()>;
+};
+} // namespace detail
+
+template <typename R, typename P>
+using OutgoingRequest = typename detail::request_t<R, P>::type;
+
+/// A function to send an outgoing event.
+template <typename P> using OutgoingEvent = typename detail::event_t<P>::type;
+
+#if __cplusplus >= 202002L
+/// This represents a protocol description that includes additional helpers
+/// for constructing requests, responses and events to work with `Binder`.
+template <typename T>
+concept BindingBuilder =
+ ProtocolDescriptor<T> &&
+ requires(T::Id id, T::Req req, T::Resp resp, T::Evt evt,
+ llvm::StringRef method, std::optional<llvm::json::Value> params,
+ std::optional<llvm::json::Value> result, llvm::Error err) {
+ /// For initializing the unique sequence identifier;
+ { T::InitialId() } -> std::same_as<typename T::Id>;
+ /// Incrementing the sequence identifier.
+ { id++ } -> std::same_as<typename T::Id>;
+
+ /// Constructing protocol types
+ /// @{
+ /// Construct a new request.
+ { T::Make(id, method, params) } -> std::same_as<typename T::Req>;
+ /// Construct a new error response.
+ { T::Make(req, std::move(err)) } -> std::same_as<typename T::Resp>;
+ /// Construct a new success response.
+ { T::Make(req, result) } -> std::same_as<typename T::Resp>;
+ /// Construct a new event.
+ { T::Make(method, params) } -> std::same_as<typename T::Evt>;
+ /// @}
+
+ /// Keys for associated types.
+ /// @{
+ /// Looking up in flight responses.
+ { T::KeyFor(resp) } -> std::same_as<typename T::Id>;
+ /// Extract method from request.
+ { T::KeyFor(req) } -> std::same_as<std::string>;
+ /// Extract method from event.
+ { T::KeyFor(evt) } -> std::same_as<std::string>;
+ /// @}
+
+ /// Extracting information from associated types.
+ /// @{
+ /// Extract parameters from a request.
+ { T::Extract(req) } -> std::same_as<std::optional<llvm::json::Value>>;
+ /// Extract result from a response.
+ { T::Extract(resp) } -> std::same_as<llvm::Expected<llvm::json::Value>>;
+ /// Extract parameters from an event.
+ { T::Extract(evt) } -> std::same_as<std::optional<llvm::json::Value>>;
+ /// @}
+ };
+#endif
+
+/// Binder collects a table of functions that handle calls.
+///
+/// The wrapper takes care of parsing/serializing responses.
+///
+/// This allows a JSONTransport to handle incoming and outgoing requests and
+/// events.
+///
+/// A bind of an incoming request to a lambda.
+/// \code{cpp}
+/// Binder binder{transport};
+/// binder.bind<int, vector<int>>("adder", [](const vector<int> &params) {
+/// int sum = 0;
+/// for (int v : params)
+/// sum += v;
+/// return sum;
+/// });
+/// \endcode
+///
+/// A bind of an outgoing request.
+/// \code{cpp}
+/// OutgoingRequest<int, vector<int>> call_add =
+/// binder.bind<int, vector<int>>("add");
+/// call_add({1,2,3}, [](Expected<int> result) {
+/// cout << *result << "\n";
+/// });
+/// \endcode
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+class Binder : public JSONTransport<Proto>::MessageHandler {
+ using Req = typename Proto::Req;
+ using Resp = typename Proto::Resp;
+ using Evt = typename Proto::Evt;
+ using Id = typename Proto::Id;
+ using Transport = JSONTransport<Proto>;
+ using MessageHandler = typename Transport::MessageHandler;
+
+public:
+ explicit Binder(Transport &transport) : m_transport(transport), m_seq(0) {}
+
+ Binder(const Binder &) = delete;
+ Binder &operator=(const Binder &) = delete;
+
+ /// Bind a handler on transport disconnect.
+ template <typename Fn, typename... Args>
+ void OnDisconnect(Fn &&fn, Args &&...args);
+
+ /// Bind a handler on error when communicating with the transport.
+ template <typename Fn, typename... Args>
+ void OnError(Fn &&fn, Args &&...args);
+
+ /// Bind a handler for an incoming request.
+ /// e.g. `bind("peek", &ThisModule::peek, this);`.
+ /// Handler should be e.g. `Expected<PeekResult> peek(const PeekParams&);`
+ /// PeekParams must be JSON parsable and PeekResult must be serializable.
+ template <typename Result, typename Params, typename Fn, typename... Args>
+ void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
+
+ /// Bind a handler for an incoming event.
+ /// e.g. `bind("peek", &ThisModule::peek, this);`
+ /// Handler should be e.g. `void peek(const PeekParams&);`
+ /// PeekParams must be JSON parsable.
+ template <typename Params, typename Fn, typename... Args>
+ void Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args);
+
+ /// Bind a function object to be used for outgoing requests.
+ /// e.g. `OutgoingRequest<Params, Result> Edit = bind("edit");`
+ /// Params must be JSON-serializable, Result must be parsable.
+ template <typename Result, typename Params>
+ OutgoingRequest<Result, Params> Bind(llvm::StringLiteral method);
+
+ /// Bind a function object to be used for outgoing events.
+ /// e.g. `OutgoingEvent<LogParams> Log = bind("log");`
+ /// LogParams must be JSON-serializable.
+ template <typename Params>
+ OutgoingEvent<Params> Bind(llvm::StringLiteral method);
+
+ void Received(const Evt &evt) override {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ auto it = m_event_handlers.find(Proto::KeyFor(evt));
+ if (it == m_event_handlers.end()) {
+ OnError(llvm::createStringError(
+ llvm::formatv("no handled for event {0}", toJSON(evt))));
+ return;
+ }
+ it->second(evt);
+ }
+
+ void Received(const Req &req) override {
+ ReplyOnce reply(req, &m_transport, this);
+
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ auto it = m_request_handlers.find(Proto::KeyFor(req));
+ if (it == m_request_handlers.end()) {
+ reply(Proto::Make(req, llvm::createStringError("method not found")));
+ return;
+ }
+
+ it->second(req, std::move(reply));
+ }
+
+ void Received(const Resp &resp) override {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+
+ Id id = Proto::KeyFor(resp);
+ auto it = m_pending_responses.find(id);
+ if (it == m_pending_responses.end()) {
+ OnError(llvm::createStringError(
+ llvm::formatv("no pending request for {0}", toJSON(resp))));
+ return;
+ }
+
+ it->second(resp);
+ m_pending_responses.erase(it);
+ }
+
+ void OnError(llvm::Error err) override {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ if (m_error_handler)
+ m_error_handler(std::move(err));
+ }
+
+ void OnClosed() override {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ if (m_disconnect_handler)
+ m_disconnect_handler();
+ }
+
+private:
+ template <typename T>
+ llvm::Expected<T> static Parse(const llvm::json::Value &raw,
+ llvm::StringRef method);
+
+ template <typename T> using Callback = llvm::unique_function<T>;
+
+ std::recursive_mutex m_mutex;
+ Transport &m_transport;
+ Id m_seq;
+ std::map<Id, Callback<void(const Resp &)>> m_pending_responses;
+ llvm::StringMap<Callback<void(const Req &, Callback<void(const Resp &)>)>>
+ m_request_handlers;
+ llvm::StringMap<Callback<void(const Evt &)>> m_event_handlers;
+ Callback<void()> m_disconnect_handler;
+ Callback<void(llvm::Error)> m_error_handler;
+
+ /// Function object to reply to a call.
+ /// Each instance must be called exactly once, otherwise:
+ /// - the bug is logged, and (in debug mode) an assert will fire
+ /// - if there was no reply, an error reply is sent
+ /// - if there were multiple replies, only the first is sent
+ class ReplyOnce {
+ std::atomic<bool> replied = {false};
+ const Req req;
+ Transport *transport; // Null when moved-from.
+ MessageHandler *handler; // Null when moved-from.
+
+ public:
+ ReplyOnce(const Req req, Transport *transport, MessageHandler *handler)
+ : req(req), transport(transport), handler(handler) {
+ assert(handler);
+ }
+ ReplyOnce(ReplyOnce &&other)
+ : replied(other.replied.load()), req(other.req),
+ transport(other.transport), handler(other.handler) {
+ other.transport = nullptr;
+ other.handler = nullptr;
+ }
+ ReplyOnce &operator=(ReplyOnce &&) = delete;
+ ReplyOnce(const ReplyOnce &) = delete;
+ ReplyOnce &operator=(const ReplyOnce &) = delete;
+
+ ~ReplyOnce() {
+ if (transport && handler && !replied) {
+ assert(false && "must reply to all calls!");
+ (*this)(Proto::Make(req, llvm::createStringError("failed to reply")));
+ }
+ }
+
+ void operator()(const Resp &resp) {
+ assert(transport && handler && "moved-from!");
+ if (replied.exchange(true)) {
+ assert(false && "must reply to each call only once!");
+ return;
+ }
+
+ if (llvm::Error error = transport->Send(resp))
+ handler->OnError(std::move(error));
+ }
+ };
+};
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Fn, typename... Args>
+void Binder<Proto>::OnDisconnect(Fn &&fn, Args &&...args) {
+ m_disconnect_handler = [fn, args...]() mutable {
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
+ };
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Fn, typename... Args>
+void Binder<Proto>::OnError(Fn &&fn, Args &&...args) {
+ m_error_handler = [fn, args...](llvm::Error error) mutable {
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...,
+ std::move(error));
+ };
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Result, typename Params, typename Fn, typename... Args>
+void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
+ assert(m_request_handlers.find(method) == m_request_handlers.end() &&
+ "request already bound");
+ if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
+ m_request_handlers[method] =
+ [fn, args...](const Req &req,
+ llvm::unique_function<void(const Resp &)> reply) mutable {
+ llvm::Error result =
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
+ reply(Proto::Make(req, std::move(result)));
+ };
+ } else if constexpr (std::is_void_v<Params>) {
+ m_request_handlers[method] =
+ [fn, args...](const Req &req,
+ llvm::unique_function<void(const Resp &)> reply) mutable {
+ llvm::Expected<Result> result =
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
+ if (!result)
+ return reply(Proto::Make(req, result.takeError()));
+ reply(Proto::Make(req, toJSON(*result)));
+ };
+ } else if constexpr (std::is_void_v<Result>) {
+ m_request_handlers[method] =
+ [method, fn,
+ args...](const Req &req,
+ llvm::unique_function<void(const Resp &)> reply) mutable {
+ llvm::Expected<Params> params =
+ Parse<Params>(Proto::Extract(req), method);
+ if (!params)
+ return reply(Proto::Make(req, params.takeError()));
+
+ llvm::Error result = std::invoke(
+ std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
+ reply(Proto::Make(req, std::move(result)));
+ };
+ } else {
+ m_request_handlers[method] =
+ [method, fn,
+ args...](const Req &req,
+ llvm::unique_function<void(const Resp &)> reply) mutable {
+ llvm::Expected<Params> params =
+ Parse<Params>(Proto::Extract(req), method);
+ if (!params)
+ return reply(Proto::Make(req, params.takeError()));
+
+ llvm::Expected<Result> result = std::invoke(
+ std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
+ if (!result)
+ return reply(Proto::Make(req, result.takeError()));
+
+ reply(Proto::Make(req, toJSON(*result)));
+ };
+ }
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Params, typename Fn, typename... Args>
+void Binder<Proto>::Bind(llvm::StringLiteral method, Fn &&fn, Args &&...args) {
+ assert(m_event_handlers.find(method) == m_event_handlers.end() &&
+ "event already bound");
+ if constexpr (std::is_void_v<Params>) {
+ m_event_handlers[method] = [fn, args...](const Evt &) mutable {
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)...);
+ };
+ } else {
+ m_event_handlers[method] = [this, method, fn,
+ args...](const Evt &evt) mutable {
+ llvm::Expected<Params> params =
+ Parse<Params>(Proto::Extract(evt), method);
+ if (!params)
+ return OnError(params.takeError());
+ std::invoke(std::forward<Fn>(fn), std::forward<Args>(args)..., *params);
+ };
+ }
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Result, typename Params>
+OutgoingRequest<Result, Params>
+Binder<Proto>::Bind(llvm::StringLiteral method) {
+ if constexpr (std::is_void_v<Result> && std::is_void_v<Params>) {
+ return [this, method](Reply<Result> fn) {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ Id id = ++m_seq;
+ Req req = Proto::Make(id, method, std::nullopt);
+ m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
+ llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
+ if (!result)
+ return fn(result.takeError());
+ fn(llvm::Error::success());
+ };
+ if (llvm::Error error = m_transport.Send(req))
+ OnError(std::move(error));
+ };
+ } else if constexpr (std::is_void_v<Params>) {
+ return [this, method](Reply<Result> fn) {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ Id id = ++m_seq;
+ Req req = Proto::Make(id, method, std::nullopt);
+ m_pending_responses[id] = [fn = std::move(fn),
+ method](const Resp &resp) mutable {
+ llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
+ if (!result)
+ return fn(result.takeError());
+ fn(Parse<Result>(*result, method));
+ };
+ if (llvm::Error error = m_transport.Send(req))
+ OnError(std::move(error));
+ };
+ } else if constexpr (std::is_void_v<Result>) {
+ return [this, method](const Params &params, Reply<Result> fn) {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ Id id = ++m_seq;
+ Req req = Proto::Make(id, method, llvm::json::Value(params));
+ m_pending_responses[id] = [fn = std::move(fn)](const Resp &resp) mutable {
+ llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
+ if (!result)
+ return fn(result.takeError());
+ fn(llvm::Error::success());
+ };
+ if (llvm::Error error = m_transport.Send(req))
+ OnError(std::move(error));
+ };
+ } else {
+ return [this, method](const Params &params, Reply<Result> fn) {
+ std::scoped_lock<std::recursive_mutex> guard(m_mutex);
+ Id id = ++m_seq;
+ Req req = Proto::Make(id, method, llvm::json::Value(params));
+ m_pending_responses[id] = [fn = std::move(fn),
+ method](const Resp &resp) mutable {
+ llvm::Expected<llvm::json::Value> result = Proto::Extract(resp);
+ if (llvm::Error err = result.takeError())
+ return fn(std::move(err));
+ fn(Parse<Result>(*result, method));
+ };
+ if (llvm::Error error = m_transport.Send(req))
+ OnError(std::move(error));
+ };
+ }
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename Params>
+OutgoingEvent<Params> Binder<Proto>::Bind(llvm::StringLiteral method) {
+ if constexpr (std::is_void_v<Params>) {
+ return [this, method]() {
+ if (llvm::Error error =
+ m_transport.Send(Proto::Make(method, std::nullopt)))
+ OnError(std::move(error));
+ };
+ } else {
+ return [this, method](const Params &params) {
+ if (llvm::Error error =
+ m_transport.Send(Proto::Make(method, toJSON(params))))
+ OnError(std::move(error));
+ };
+ }
+}
+
+#if __cplusplus >= 202002L
+template <BindingBuilder Proto>
+#else
+template <typename Proto>
+#endif
+template <typename T>
+llvm::Expected<T> Binder<Proto>::Parse(const llvm::json::Value &raw,
+ llvm::StringRef method) {
+ T result;
+ llvm::json::Path::Root root;
+ if (!fromJSON(raw, result, root)) {
+ // Dump the relevant parts of the broken message.
+ std::string context;
+ llvm::raw_string_ostream OS(context);
+ root.printErrorContext(raw, OS);
+ return llvm::make_error<InvalidParams>(method.str(), context);
+ }
+ return std::move(result);
+}
+
+} // namespace lldb_private::transport
#endif
diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
index 28d6ed992b14..d29fd8126e0c 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h
@@ -26,6 +26,16 @@ public:
virtual bool ResolverCallback(SymbolContext sym_ctx) { return true; }
virtual lldb::SearchDepth GetDepth() { return lldb::eSearchDepthModule; }
virtual std::optional<std::string> GetShortHelp() { return nullptr; }
+ /// WasHit returns the breakpoint location SP for the location that was "hit".
+ virtual lldb::BreakpointLocationSP
+ WasHit(lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) {
+ return LLDB_INVALID_BREAK_ID;
+ }
+ virtual std::optional<std::string>
+ GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
+ lldb::DescriptionLevel level) {
+ return {};
+ }
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 024bbc90a9a3..6c0054a1ec1d 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -11,6 +11,7 @@
#include "lldb/API/SBAttachInfo.h"
#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBBreakpointLocation.h"
#include "lldb/API/SBData.h"
#include "lldb/API/SBError.h"
#include "lldb/API/SBEvent.h"
@@ -572,12 +573,17 @@ public:
lldb::StreamSP GetOpaqueTypeFromSBStream(const lldb::SBStream &stream) const;
+ lldb::StackFrameSP GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const;
+
SymbolContext
GetOpaqueTypeFromSBSymbolContext(const lldb::SBSymbolContext &sym_ctx) const;
lldb::BreakpointSP
GetOpaqueTypeFromSBBreakpoint(const lldb::SBBreakpoint &breakpoint) const;
+ lldb::BreakpointLocationSP GetOpaqueTypeFromSBBreakpointLocation(
+ const lldb::SBBreakpointLocation &break_loc) const;
+
lldb::ProcessAttachInfoSP
GetOpaqueTypeFromSBAttachInfo(const lldb::SBAttachInfo &attach_info) const;
diff --git a/lldb/include/lldb/Protocol/MCP/MCPError.h b/lldb/include/lldb/Protocol/MCP/MCPError.h
index 55dd40f124a1..609a1733197d 100644
--- a/lldb/include/lldb/Protocol/MCP/MCPError.h
+++ b/lldb/include/lldb/Protocol/MCP/MCPError.h
@@ -9,7 +9,6 @@
#ifndef LLDB_PROTOCOL_MCP_MCPERROR_H
#define LLDB_PROTOCOL_MCP_MCPERROR_H
-#include "lldb/Protocol/MCP/Protocol.h"
#include "llvm/Support/Error.h"
#include <string>
@@ -26,14 +25,12 @@ public:
const std::string &getMessage() const { return m_message; }
- lldb_protocol::mcp::Error toProtocolError() const;
-
static constexpr int64_t kResourceNotFound = -32002;
static constexpr int64_t kInternalError = -32603;
private:
std::string m_message;
- int64_t m_error_code;
+ int m_error_code;
};
class UnsupportedURI : public llvm::ErrorInfo<UnsupportedURI> {
diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h
index 6e1ffcbe1f3e..a0ba8659ffe2 100644
--- a/lldb/include/lldb/Protocol/MCP/Protocol.h
+++ b/lldb/include/lldb/Protocol/MCP/Protocol.h
@@ -14,6 +14,7 @@
#ifndef LLDB_PROTOCOL_MCP_PROTOCOL_H
#define LLDB_PROTOCOL_MCP_PROTOCOL_H
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/JSON.h"
#include <optional>
#include <string>
@@ -322,6 +323,10 @@ struct CallToolResult {
llvm::json::Value toJSON(const CallToolResult &);
bool fromJSON(const llvm::json::Value &, CallToolResult &, llvm::json::Path);
+lldb_protocol::mcp::Request
+MakeRequest(int64_t id, llvm::StringRef method,
+ std::optional<llvm::json::Value> params);
+
} // namespace lldb_protocol::mcp
#endif
diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h
index 970980d075ea..f185d51f4119 100644
--- a/lldb/include/lldb/Protocol/MCP/Server.h
+++ b/lldb/include/lldb/Protocol/MCP/Server.h
@@ -9,7 +9,6 @@
#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"
@@ -19,75 +18,66 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JSON.h"
#include "llvm/Support/Signals.h"
-#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace lldb_protocol::mcp {
-class Server : public MCPTransport::MessageHandler {
- using ClosedCallback = llvm::unique_function<void()>;
+class Server {
+
+ using MCPTransportUP = std::unique_ptr<lldb_protocol::mcp::MCPTransport>;
+
+ using ReadHandleUP = lldb_private::MainLoop::ReadHandleUP;
public:
- Server(std::string name, std::string version, MCPTransport &client,
- LogCallback log_callback = {}, ClosedCallback closed_callback = {});
+ Server(std::string name, std::string version, LogCallback log_callback = {});
~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);
-
-protected:
- ServerCapabilities GetCapabilities();
-
- using RequestHandler =
- std::function<llvm::Expected<Response>(const Request &)>;
- void AddRequestHandlers();
+ llvm::Error Accept(lldb_private::MainLoop &, MCPTransportUP);
- 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);
+protected:
+ MCPBinderUP Bind(MCPTransport &);
- llvm::Expected<Response> InitializeHandler(const Request &);
+ ServerCapabilities GetCapabilities();
- llvm::Expected<Response> ToolsListHandler(const Request &);
- llvm::Expected<Response> ToolsCallHandler(const Request &);
+ llvm::Expected<InitializeResult> InitializeHandler(const InitializeParams &);
- llvm::Expected<Response> ResourcesListHandler(const Request &);
- llvm::Expected<Response> ResourcesReadHandler(const Request &);
+ llvm::Expected<ListToolsResult> ToolsListHandler();
+ llvm::Expected<CallToolResult> ToolsCallHandler(const CallToolParams &);
- void Received(const Request &) override;
- void Received(const Response &) override;
- void Received(const Notification &) override;
- void OnError(llvm::Error) override;
- void OnClosed() override;
+ llvm::Expected<ListResourcesResult> ResourcesListHandler();
+ llvm::Expected<ReadResourceResult>
+ ResourcesReadHandler(const ReadResourceParams &);
-protected:
- void Log(llvm::StringRef);
+ template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
+ Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
+ }
+ void Log(llvm::StringRef message) {
+ if (m_log_callback)
+ m_log_callback(message);
+ }
private:
const std::string m_name;
const std::string m_version;
- MCPTransport &m_client;
LogCallback m_log_callback;
- ClosedCallback m_closed_callback;
+ struct Client {
+ ReadHandleUP handle;
+ MCPTransportUP transport;
+ MCPBinderUP binder;
+ };
+ std::map<MCPTransport *, Client> m_instances;
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;
};
class ServerInfoHandle;
@@ -121,7 +111,7 @@ public:
ServerInfoHandle &operator=(const ServerInfoHandle &) = delete;
/// @}
- /// Remove the file.
+ /// Remove the file on disk, if one is tracked.
void Remove();
private:
diff --git a/lldb/include/lldb/Protocol/MCP/Transport.h b/lldb/include/lldb/Protocol/MCP/Transport.h
index 47c2ccfc44df..b7a1eb778d66 100644
--- a/lldb/include/lldb/Protocol/MCP/Transport.h
+++ b/lldb/include/lldb/Protocol/MCP/Transport.h
@@ -10,22 +10,78 @@
#define LLDB_PROTOCOL_MCP_TRANSPORT_H
#include "lldb/Host/JSONTransport.h"
+#include "lldb/Protocol/MCP/MCPError.h"
#include "lldb/Protocol/MCP/Protocol.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include <sys/types.h>
namespace lldb_protocol::mcp {
+struct ProtocolDescriptor {
+ using Id = int64_t;
+ using Req = Request;
+ using Resp = Response;
+ using Evt = Notification;
+
+ static inline Id InitialId() { return 0; }
+ static inline Request Make(Id id, llvm::StringRef method,
+ std::optional<llvm::json::Value> params) {
+ return Request{id, method.str(), params};
+ }
+ static inline Notification Make(llvm::StringRef method,
+ std::optional<llvm::json::Value> params) {
+ return Notification{method.str(), params};
+ }
+ static inline Response Make(Req req, llvm::Error error) {
+ lldb_protocol::mcp::Error protocol_error;
+ llvm::handleAllErrors(
+ std::move(error), [&](const llvm::ErrorInfoBase &err) {
+ std::error_code cerr = err.convertToErrorCode();
+ protocol_error.code =
+ cerr == llvm::inconvertibleErrorCode()
+ ? lldb_protocol::mcp::eErrorCodeInternalError
+ : cerr.value();
+ protocol_error.message = err.message();
+ });
+
+ return Response{req.id, std::move(protocol_error)};
+ }
+ static inline Response Make(Req req,
+ std::optional<llvm::json::Value> result) {
+ return Response{req.id, std::move(result)};
+ }
+ static inline Id KeyFor(Response r) { return std::get<Id>(r.id); }
+ static inline std::string KeyFor(Request r) { return r.method; }
+ static inline std::string KeyFor(Notification n) { return n.method; }
+ static inline std::optional<llvm::json::Value> Extract(Request r) {
+ return r.params;
+ }
+ static inline llvm::Expected<llvm::json::Value> Extract(Response r) {
+ if (const lldb_protocol::mcp::Error *error =
+ std::get_if<lldb_protocol::mcp::Error>(&r.result))
+ return llvm::make_error<lldb_protocol::mcp::MCPError>(error->message,
+ error->code);
+ return std::get<llvm::json::Value>(r.result);
+ }
+ static inline std::optional<llvm::json::Value> Extract(Notification n) {
+ return n.params;
+ }
+};
+
/// Generic transport that uses the MCP protocol.
-using MCPTransport = lldb_private::Transport<Request, Response, Notification>;
+using MCPTransport = lldb_private::transport::JSONTransport<ProtocolDescriptor>;
+using MCPBinder = lldb_private::transport::Binder<ProtocolDescriptor>;
+using MCPBinderUP = std::unique_ptr<MCPBinder>;
/// Generic logging callback, to allow the MCP server / client / transport layer
/// to be independent of the lldb log implementation.
using LogCallback = llvm::unique_function<void(llvm::StringRef message)>;
class Transport final
- : public lldb_private::JSONRPCTransport<Request, Response, Notification> {
+ : public lldb_private::transport::JSONRPCTransport<ProtocolDescriptor> {
public:
Transport(lldb::IOObjectSP in, lldb::IOObjectSP out,
LogCallback log_callback = {});
diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h
index 3d0aa326d5a6..9958b6ea2f81 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -166,7 +166,7 @@ public:
llvm::StringRef file_path);
// return false from callback to stop iterating
- static void ForEach(std::function<bool(Language *)> callback);
+ static void ForEach(llvm::function_ref<IterationAction(Language *)> callback);
virtual lldb::LanguageType GetLanguageType() const = 0;
@@ -404,8 +404,15 @@ public:
GetLanguageTypeFromString(const char *string) = delete;
static lldb::LanguageType GetLanguageTypeFromString(llvm::StringRef string);
+ /// Returns the internal LLDB name for the specified language. When presenting
+ /// the language name to users, use \ref GetDisplayNameForLanguageType
+ /// instead.
static const char *GetNameForLanguageType(lldb::LanguageType language);
+ /// Returns a user-friendly name for the specified language.
+ static llvm::StringRef
+ GetDisplayNameForLanguageType(lldb::LanguageType language);
+
static void PrintAllLanguages(Stream &s, const char *prefix,
const char *suffix);
@@ -420,7 +427,8 @@ public:
llvm::StringRef suffix);
// return false from callback to stop iterating
- static void ForAllLanguages(std::function<bool(lldb::LanguageType)> callback);
+ static void ForAllLanguages(
+ llvm::function_ref<IterationAction(lldb::LanguageType)> callback);
static bool LanguageIsCPlusPlus(lldb::LanguageType language);
diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index d6983bb0b9d2..2653835206ec 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -322,12 +322,14 @@ public:
void IncreaseSourceRealpathCompatibleCount(uint32_t count);
StatsDuration &GetCreateTime() { return m_create_time; }
+ StatsDuration &GetLoadCoreTime() { return m_load_core_time; }
StatsSuccessFail &GetExpressionStats() { return m_expr_eval; }
StatsSuccessFail &GetFrameVariableStats() { return m_frame_var; }
void Reset(Target &target);
protected:
StatsDuration m_create_time;
+ StatsDuration m_load_core_time;
std::optional<StatsTimepoint> m_launch_or_attach_time;
std::optional<StatsTimepoint> m_first_private_stop_time;
std::optional<StatsTimepoint> m_first_public_stop_time;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 14a09f29094d..f4a09237ce89 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -600,6 +600,17 @@ public:
bool IsDummyTarget() const { return m_is_dummy_target; }
+ /// Get the globally unique ID for this target.
+ ///
+ /// This ID is unique across all debugger instances and all targets,
+ /// within the same lldb process. The ID is assigned
+ /// during target construction and remains constant for the target's lifetime.
+ /// The first target created (typically the dummy target) gets ID 1.
+ ///
+ /// \return
+ /// The globally unique ID for this target.
+ lldb::user_id_t GetGloballyUniqueID() const { return m_target_unique_id; }
+
const std::string &GetLabel() const { return m_label; }
/// Set a label for a target.
@@ -1651,6 +1662,9 @@ protected:
bool m_suppress_stop_hooks; /// Used to not run stop hooks for expressions
bool m_is_dummy_target;
unsigned m_next_persistent_variable_index = 0;
+ lldb::user_id_t m_target_unique_id =
+ LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID; /// The globally unique ID
+ /// assigned to this target
/// An optional \a lldb_private::Trace object containing processor trace
/// information of this target.
lldb::TraceSP m_trace_sp;
diff --git a/lldb/include/lldb/Target/TargetList.h b/lldb/include/lldb/Target/TargetList.h
index 080a6039c7ff..88272512bcc0 100644
--- a/lldb/include/lldb/Target/TargetList.h
+++ b/lldb/include/lldb/Target/TargetList.h
@@ -159,6 +159,17 @@ public:
lldb::TargetSP FindTargetWithProcess(lldb_private::Process *process) const;
+ /// Find the target that has a globally unique ID that matches ID \a id.
+ ///
+ /// \param[in] id
+ /// The globally unique target ID to search our target list for.
+ ///
+ /// \return
+ /// A shared pointer to a target object. The returned shared
+ /// pointer will contain nullptr if no target objects has a
+ /// matching target ID.
+ lldb::TargetSP FindTargetByGloballyUniqueID(lldb::user_id_t id) const;
+
lldb::TargetSP GetTargetSP(Target *target) const;
/// Send an async interrupt to one or all processes.
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 7db184ad6722..aaaf94c6c5f7 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -72,6 +72,28 @@
#define ANSI_ESC_START_LEN 2
+// Cursor Position, set cursor to position [l, c] (default = [1, 1]).
+#define ANSI_CSI_CUP(...) ANSI_ESC_START #__VA_ARGS__ "H"
+// Reset cursor to position.
+#define ANSI_CSI_RESET_CURSOR ANSI_CSI_CUP()
+// Erase In Display.
+#define ANSI_CSI_ED(opt) ANSI_ESC_START #opt "J"
+// Erase complete viewport.
+#define ANSI_CSI_ERASE_VIEWPORT ANSI_CSI_ED(2)
+// Erase scrollback.
+#define ANSI_CSI_ERASE_SCROLLBACK ANSI_CSI_ED(3)
+
+// OSC (Operating System Commands)
+// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+#define OSC_ESCAPE_START "\033"
+#define OSC_ESCAPE_END "\x07"
+
+// https://conemu.github.io/en/AnsiEscapeCodes.html#ConEmu_specific_OSC
+#define OSC_PROGRESS_REMOVE OSC_ESCAPE_START "]9;4;0;0" OSC_ESCAPE_END
+#define OSC_PROGRESS_SHOW OSC_ESCAPE_START "]9;4;1;%u" OSC_ESCAPE_END
+#define OSC_PROGRESS_ERROR OSC_ESCAPE_START "]9;4;2;%u" OSC_ESCAPE_END
+#define OSC_PROGRESS_INDETERMINATE OSC_ESCAPE_START "]9;4;3;%u" OSC_ESCAPE_END
+
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
diff --git a/lldb/include/lldb/Utility/DataExtractor.h b/lldb/include/lldb/Utility/DataExtractor.h
index 0b7e771ed4f8..b4960f5e87c8 100644
--- a/lldb/include/lldb/Utility/DataExtractor.h
+++ b/lldb/include/lldb/Utility/DataExtractor.h
@@ -994,7 +994,7 @@ protected:
constexpr size_t src_size = sizeof(T);
T val = fail_value;
- const T *src = static_cast<const T *>(GetData(offset_ptr, src_size));
+ const void *src = GetData(offset_ptr, src_size);
if (!src)
return val;
diff --git a/lldb/include/lldb/Utility/Stream.h b/lldb/include/lldb/Utility/Stream.h
index fc547ed73923..82774d56922a 100644
--- a/lldb/include/lldb/Utility/Stream.h
+++ b/lldb/include/lldb/Utility/Stream.h
@@ -260,16 +260,8 @@ public:
/// \param[in] text
/// The string to be output to the stream.
///
- /// \param[in] pattern
- /// The regex pattern to match against the \a text string. Portions of \a
- /// text matching this pattern will be colorized. If this parameter is
- /// nullptr, highlighting is not performed.
- /// \param[in] prefix
- /// The ANSI color code to start colorization. This is
- /// environment-dependent.
- /// \param[in] suffix
- /// The ANSI color code to end colorization. This is
- /// environment-dependent.
+ /// \param[in] settings
+ /// Optional print hilight settings.
void PutCStringColorHighlighted(
llvm::StringRef text,
std::optional<HighlightSettings> settings = std::nullopt);
diff --git a/lldb/include/lldb/Utility/XcodeSDK.h b/lldb/include/lldb/Utility/XcodeSDK.h
index 5b345a4965cf..5f8901953768 100644
--- a/lldb/include/lldb/Utility/XcodeSDK.h
+++ b/lldb/include/lldb/Utility/XcodeSDK.h
@@ -38,7 +38,7 @@ public:
watchOS,
XRSimulator,
XROS,
- bridgeOS,
+ BridgeOS,
Linux,
unknown = -1
};
diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h
index c7bd019c5c90..c54ef884b01d 100644
--- a/lldb/include/lldb/lldb-defines.h
+++ b/lldb/include/lldb/lldb-defines.h
@@ -96,6 +96,7 @@
#define LLDB_INVALID_QUEUE_ID 0
#define LLDB_INVALID_CPU_ID UINT32_MAX
#define LLDB_INVALID_WATCHPOINT_RESOURCE_ID UINT32_MAX
+#define LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID 0
/// CPU Type definitions
#define LLDB_ARCH_DEFAULT "systemArch"
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index fec9fdef44df..1a7db8faecd9 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -130,6 +130,8 @@ FLAGS_ENUM(LaunchFlags){
eLaunchFlagInheritTCCFromParent =
(1u << 12), ///< Don't make the inferior responsible for its own TCC
///< permissions but instead inherit them from its parent.
+ eLaunchFlagMemoryTagging =
+ (1u << 13), ///< Launch process with memory tagging explicitly enabled.
};
/// Thread Run Modes.
diff --git a/lldb/packages/Python/lldbsuite/support/seven.py b/lldb/packages/Python/lldbsuite/support/seven.py
index 1b96658b68ed..8e621ba1ee91 100644
--- a/lldb/packages/Python/lldbsuite/support/seven.py
+++ b/lldb/packages/Python/lldbsuite/support/seven.py
@@ -1,5 +1,4 @@
import binascii
-import shlex
import subprocess
@@ -38,8 +37,3 @@ def unhexlify(hexstr):
def hexlify(data):
"""Hex-encode string data. The result if always a string."""
return bitcast_to_string(binascii.hexlify(bitcast_to_bytes(data)))
-
-
-# TODO: Replace this with `shlex.join` when minimum Python version is >= 3.8
-def join_for_shell(split_command):
- return " ".join([shlex.quote(part) for part in split_command])
diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
index b46a5acc596f..d7668c1884e4 100644
--- a/lldb/packages/Python/lldbsuite/test/cpu_feature.py
+++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
@@ -62,7 +62,7 @@ class CPUFeature:
class AArch64:
FPMR = CPUFeature("fpmr")
GCS = CPUFeature("gcs")
- MTE = CPUFeature("mte")
+ MTE = CPUFeature("mte", "hw.optional.arm.FEAT_MTE4")
MTE_STORE_ONLY = CPUFeature("mtestoreonly")
PTR_AUTH = CPUFeature("paca", "hw.optional.arm.FEAT_PAuth2")
SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME")
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 2966ac04227c..63f7df4de189 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -280,9 +280,6 @@ def parseOptionsAndInitTestdirs():
configuration.llvm_tools_dir = args.llvm_tools_dir
configuration.filecheck = shutil.which("FileCheck", path=args.llvm_tools_dir)
configuration.yaml2obj = shutil.which("yaml2obj", path=args.llvm_tools_dir)
- configuration.yaml2macho_core = shutil.which(
- "yaml2macho-core", path=args.llvm_tools_dir
- )
if not configuration.get_filecheck_path():
logging.warning("No valid FileCheck executable; some tests may fail...")
@@ -563,6 +560,8 @@ def setupSysPath():
if is_exe(lldbDAPExec):
os.environ["LLDBDAP_EXEC"] = lldbDAPExec
+ configuration.yaml2macho_core = shutil.which("yaml2macho-core", path=lldbDir)
+
lldbPythonDir = None # The directory that contains 'lldb/__init__.py'
# If our lldb supports the -P option, use it to find the python path:
@@ -789,6 +788,10 @@ def canRunLibcxxTests():
return True, "libc++ always present"
if platform == "linux":
+ if not configuration.libcxx_include_dir or not configuration.libcxx_library_dir:
+ return False, "API tests require a locally built libc++."
+
+ # Make sure -stdlib=libc++ works since that's how the tests will be built.
with temp_file.OnDiskTempFile() as f:
cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.path, "-"]
p = subprocess.Popen(
diff --git a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py
index b603c35c8df0..1a2860a32743 100644
--- a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py
+++ b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py
@@ -1,3 +1,4 @@
+from abc import ABC, abstractmethod
import ctypes
import errno
import io
@@ -5,6 +6,7 @@ import threading
import socket
import traceback
from lldbsuite.support import seven
+from typing import Optional, List, Tuple
def checksum(message):
@@ -86,8 +88,7 @@ class MockGDBServerResponder:
handles any packet not recognized in the common packet handling code.
"""
- registerCount = 40
- packetLog = None
+ registerCount: int = 40
class RESPONSE_DISCONNECT:
pass
@@ -96,7 +97,7 @@ class MockGDBServerResponder:
pass
def __init__(self):
- self.packetLog = []
+ self.packetLog: List[str] = []
def respond(self, packet):
"""
@@ -242,7 +243,7 @@ class MockGDBServerResponder:
def qHostInfo(self):
return "ptrsize:8;endian:little;"
- def qEcho(self):
+ def qEcho(self, num: int):
return "E04"
def qQueryGDBServer(self):
@@ -263,10 +264,10 @@ class MockGDBServerResponder:
def D(self, packet):
return "OK"
- def readRegisters(self):
+ def readRegisters(self) -> str:
return "00000000" * self.registerCount
- def readRegister(self, register):
+ def readRegister(self, register: int) -> str:
return "00000000"
def writeRegisters(self, registers_hex):
@@ -306,7 +307,9 @@ class MockGDBServerResponder:
# SIGINT is 2, return type is 2 digit hex string
return "S02"
- def qXferRead(self, obj, annex, offset, length):
+ def qXferRead(
+ self, obj: str, annex: str, offset: int, length: int
+ ) -> Tuple[Optional[str], bool]:
return None, False
def _qXferResponse(self, data, has_more):
@@ -374,15 +377,17 @@ class MockGDBServerResponder:
pass
-class ServerChannel:
+class ServerChannel(ABC):
"""
A wrapper class for TCP or pty-based server.
"""
- def get_connect_address(self):
+ @abstractmethod
+ def get_connect_address(self) -> str:
"""Get address for the client to connect to."""
- def get_connect_url(self):
+ @abstractmethod
+ def get_connect_url(self) -> str:
"""Get URL suitable for process connect command."""
def close_server(self):
@@ -394,10 +399,12 @@ class ServerChannel:
def close_connection(self):
"""Close all resources used by the accepted connection."""
- def recv(self):
+ @abstractmethod
+ def recv(self) -> bytes:
"""Receive a data packet from the connected client."""
- def sendall(self, data):
+ @abstractmethod
+ def sendall(self, data: bytes) -> None:
"""Send the data to the connected client."""
@@ -428,11 +435,11 @@ class ServerSocket(ServerChannel):
self._connection.close()
self._connection = None
- def recv(self):
+ def recv(self) -> bytes:
assert self._connection is not None
return self._connection.recv(4096)
- def sendall(self, data):
+ def sendall(self, data: bytes) -> None:
assert self._connection is not None
return self._connection.sendall(data)
@@ -444,10 +451,10 @@ class TCPServerSocket(ServerSocket):
)[0]
super().__init__(family, type, proto, addr)
- def get_connect_address(self):
+ def get_connect_address(self) -> str:
return "[{}]:{}".format(*self._server_socket.getsockname())
- def get_connect_url(self):
+ def get_connect_url(self) -> str:
return "connect://" + self.get_connect_address()
@@ -455,10 +462,10 @@ class UnixServerSocket(ServerSocket):
def __init__(self, addr):
super().__init__(socket.AF_UNIX, socket.SOCK_STREAM, 0, addr)
- def get_connect_address(self):
+ def get_connect_address(self) -> str:
return self._server_socket.getsockname()
- def get_connect_url(self):
+ def get_connect_url(self) -> str:
return "unix-connect://" + self.get_connect_address()
@@ -472,7 +479,7 @@ class PtyServerSocket(ServerChannel):
self._primary = io.FileIO(primary, "r+b")
self._secondary = io.FileIO(secondary, "r+b")
- def get_connect_address(self):
+ def get_connect_address(self) -> str:
libc = ctypes.CDLL(None)
libc.ptsname.argtypes = (ctypes.c_int,)
libc.ptsname.restype = ctypes.c_char_p
@@ -485,7 +492,7 @@ class PtyServerSocket(ServerChannel):
self._secondary.close()
self._primary.close()
- def recv(self):
+ def recv(self) -> bytes:
try:
return self._primary.read(4096)
except OSError as e:
@@ -494,8 +501,8 @@ class PtyServerSocket(ServerChannel):
return b""
raise
- def sendall(self, data):
- return self._primary.write(data)
+ def sendall(self, data: bytes) -> None:
+ self._primary.write(data)
class MockGDBServer:
@@ -528,18 +535,21 @@ class MockGDBServer:
self._thread.join()
self._thread = None
- def get_connect_address(self):
+ def get_connect_address(self) -> str:
+ assert self._socket is not None
return self._socket.get_connect_address()
- def get_connect_url(self):
+ def get_connect_url(self) -> str:
+ assert self._socket is not None
return self._socket.get_connect_url()
def run(self):
+ assert self._socket is not None
# For testing purposes, we only need to worry about one client
# connecting just one time.
try:
self._socket.accept()
- except:
+ except Exception:
traceback.print_exc()
return
self._shouldSendAck = True
@@ -554,7 +564,7 @@ class MockGDBServer:
self._receive(data)
except self.TerminateConnectionException:
pass
- except Exception as e:
+ except Exception:
print(
"An exception happened when receiving the response from the gdb server. Closing the client..."
)
@@ -587,7 +597,9 @@ class MockGDBServer:
Once a complete packet is found at the front of self._receivedData,
its data is removed form self._receivedData.
"""
+ assert self._receivedData is not None
data = self._receivedData
+ assert self._receivedDataOffset is not None
i = self._receivedDataOffset
data_len = len(data)
if data_len == 0:
@@ -640,10 +652,13 @@ class MockGDBServer:
self._receivedDataOffset = 0
return packet
- def _sendPacket(self, packet):
- self._socket.sendall(seven.bitcast_to_bytes(frame_packet(packet)))
+ def _sendPacket(self, packet: str):
+ assert self._socket is not None
+ framed_packet = seven.bitcast_to_bytes(frame_packet(packet))
+ self._socket.sendall(framed_packet)
def _handlePacket(self, packet):
+ assert self._socket is not None
if packet is self.PACKET_ACK:
# Ignore ACKs from the client. For the future, we can consider
# adding validation code to make sure the client only sends ACKs
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 807492272344..b92de941c412 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -36,6 +36,7 @@ import json
import os.path
import re
import shutil
+import shlex
import signal
from subprocess import *
import sys
@@ -56,7 +57,6 @@ from . import lldbutil
from . import test_categories
from lldbsuite.support import encoded_file
from lldbsuite.support import funcutils
-from lldbsuite.support import seven
from lldbsuite.test_event import build_exception
# See also dotest.parseOptionsAndInitTestdirs(), where the environment variables
@@ -1508,7 +1508,7 @@ class Base(unittest.TestCase):
self.runBuildCommand(command)
def runBuildCommand(self, command):
- self.trace(seven.join_for_shell(command))
+ self.trace(shlex.join(command))
try:
output = check_output(command, stderr=STDOUT, errors="replace")
except CalledProcessError as cpe:
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 8eb64b4ab8b2..a3d924d495fb 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
@@ -27,6 +27,10 @@ from typing import (
Literal,
)
+# set timeout based on whether ASAN was enabled or not. Increase
+# timeout by a factor of 10 if ASAN is enabled.
+DEFAULT_TIMEOUT = 10 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
+
## DAP type references
@@ -282,26 +286,24 @@ class DebugCommunication(object):
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
+ deadline = time.monotonic() + DEFAULT_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())
+ event = self.wait_for_event(["output"])
if not event: # Timeout or EOF
break
output += self.get_output(category, clear=clear)
@@ -339,7 +341,7 @@ class DebugCommunication(object):
self,
*,
predicate: Optional[Callable[[ProtocolMessage], bool]] = None,
- timeout: Optional[float] = None,
+ timeout: Optional[float] = DEFAULT_TIMEOUT,
) -> Optional[ProtocolMessage]:
"""Processes received packets from the adapter.
Updates the DebugCommunication stateful properties based on the received
@@ -555,25 +557,20 @@ class DebugCommunication(object):
return cast(Optional[Response], self._recv_packet(predicate=predicate))
- def wait_for_event(
- self, filter: List[str] = [], timeout: Optional[float] = None
- ) -> Optional[Event]:
+ def wait_for_event(self, filter: List[str] = []) -> Optional[Event]:
"""Wait for the first event that matches the filter."""
def predicate(p: ProtocolMessage):
return p["type"] == "event" and p["event"] in filter
return cast(
- Optional[Event], self._recv_packet(predicate=predicate, timeout=timeout)
+ Optional[Event],
+ self._recv_packet(predicate=predicate),
)
- def wait_for_stopped(
- self, timeout: Optional[float] = None
- ) -> Optional[List[Event]]:
+ def wait_for_stopped(self) -> Optional[List[Event]]:
stopped_events = []
- stopped_event = self.wait_for_event(
- filter=["stopped", "exited"], timeout=timeout
- )
+ stopped_event = self.wait_for_event(filter=["stopped", "exited"])
while stopped_event:
stopped_events.append(stopped_event)
# If we exited, then we are done
@@ -582,26 +579,28 @@ class DebugCommunication(object):
# Otherwise we stopped and there might be one or more 'stopped'
# events for each thread that stopped with a reason, so keep
# checking for more 'stopped' events and return all of them
- stopped_event = self.wait_for_event(
- filter=["stopped", "exited"], timeout=0.25
+ # Use a shorter timeout for additional stopped events
+ def predicate(p: ProtocolMessage):
+ return p["type"] == "event" and p["event"] in ["stopped", "exited"]
+
+ stopped_event = cast(
+ Optional[Event], self._recv_packet(predicate=predicate, timeout=0.25)
)
return stopped_events
- def wait_for_breakpoint_events(self, timeout: Optional[float] = None):
+ def wait_for_breakpoint_events(self):
breakpoint_events: list[Event] = []
while True:
- event = self.wait_for_event(["breakpoint"], timeout=timeout)
+ event = self.wait_for_event(["breakpoint"])
if not event:
break
breakpoint_events.append(event)
return breakpoint_events
- def wait_for_breakpoints_to_be_verified(
- self, breakpoint_ids: list[str], timeout: Optional[float] = None
- ):
+ def wait_for_breakpoints_to_be_verified(self, breakpoint_ids: list[str]):
"""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"])
if breakpoint_event is None:
break
@@ -614,14 +613,14 @@ class DebugCommunication(object):
)
]
- def wait_for_exited(self, timeout: Optional[float] = None):
- event_dict = self.wait_for_event(["exited"], timeout=timeout)
+ def wait_for_exited(self):
+ event_dict = self.wait_for_event(["exited"])
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)
+ def wait_for_terminated(self):
+ event_dict = self.wait_for_event(["terminated"])
if event_dict is None:
raise ValueError("didn't get terminated event")
return event_dict
@@ -1610,7 +1609,7 @@ class DebugAdapterServer(DebugCommunication):
# new messages will arrive and it should shutdown on its
# own.
process.stdin.close()
- process.wait(timeout=20)
+ process.wait(timeout=DEFAULT_TIMEOUT)
except subprocess.TimeoutExpired:
process.kill()
process.wait()
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 f7b1ed80fceb..29935bb8046f 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
@@ -18,7 +18,7 @@ import base64
class DAPTestCaseBase(TestBase):
# set timeout based on whether ASAN was enabled or not. Increase
# timeout by a factor of 10 if ASAN is enabled.
- DEFAULT_TIMEOUT = 10 * (10 if ("ASAN_OPTIONS" in os.environ) else 1)
+ DEFAULT_TIMEOUT = dap_server.DEFAULT_TIMEOUT
NO_DEBUG_INFO_TESTCASE = True
def create_debug_adapter(
@@ -118,11 +118,9 @@ class DAPTestCaseBase(TestBase):
self.wait_for_breakpoints_to_resolve(breakpoint_ids)
return breakpoint_ids
- def wait_for_breakpoints_to_resolve(
- self, breakpoint_ids: list[str], timeout: Optional[float] = DEFAULT_TIMEOUT
- ):
+ def wait_for_breakpoints_to_resolve(self, breakpoint_ids: list[str]):
unresolved_breakpoints = self.dap_server.wait_for_breakpoints_to_be_verified(
- breakpoint_ids, timeout
+ breakpoint_ids
)
self.assertEqual(
len(unresolved_breakpoints),
@@ -134,11 +132,10 @@ class DAPTestCaseBase(TestBase):
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
+ deadline = time.monotonic() + self.DEFAULT_TIMEOUT
while deadline > time.monotonic():
if predicate():
return True
@@ -155,15 +152,13 @@ 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: List[Union[int, str]], timeout: float = DEFAULT_TIMEOUT
- ):
+ def verify_breakpoint_hit(self, breakpoint_ids: List[Union[int, str]]):
"""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)
+ stopped_events = self.dap_server.wait_for_stopped()
normalized_bp_ids = [str(b) for b in breakpoint_ids]
for stopped_event in stopped_events:
if "body" in stopped_event:
@@ -186,11 +181,11 @@ class DAPTestCaseBase(TestBase):
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):
+ def verify_all_breakpoints_hit(self, breakpoint_ids):
"""Wait for the process we are debugging to stop, and verify we hit
all of the breakpoint locations in the "breakpoint_ids" array.
"breakpoint_ids" should be a list of int breakpoint IDs ([1, 2])."""
- stopped_events = self.dap_server.wait_for_stopped(timeout)
+ stopped_events = self.dap_server.wait_for_stopped()
for stopped_event in stopped_events:
if "body" in stopped_event:
body = stopped_event["body"]
@@ -208,12 +203,12 @@ class DAPTestCaseBase(TestBase):
return
self.assertTrue(False, f"breakpoints not hit, stopped_events={stopped_events}")
- def verify_stop_exception_info(self, expected_description, timeout=DEFAULT_TIMEOUT):
+ def verify_stop_exception_info(self, expected_description):
"""Wait for the process we are debugging to stop, and verify the stop
reason is 'exception' and that the description matches
'expected_description'
"""
- stopped_events = self.dap_server.wait_for_stopped(timeout)
+ stopped_events = self.dap_server.wait_for_stopped()
for stopped_event in stopped_events:
if "body" in stopped_event:
body = stopped_event["body"]
@@ -338,26 +333,14 @@ class DAPTestCaseBase(TestBase):
def get_important(self):
return self.dap_server.get_output("important")
- def collect_stdout(
- self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
- ) -> str:
- return self.dap_server.collect_output(
- "stdout", timeout=timeout, pattern=pattern
- )
+ def collect_stdout(self, pattern: Optional[str] = None) -> str:
+ return self.dap_server.collect_output("stdout", pattern=pattern)
- def collect_console(
- self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
- ) -> str:
- return self.dap_server.collect_output(
- "console", timeout=timeout, pattern=pattern
- )
+ def collect_console(self, pattern: Optional[str] = None) -> str:
+ return self.dap_server.collect_output("console", pattern=pattern)
- def collect_important(
- self, timeout: float = DEFAULT_TIMEOUT, pattern: Optional[str] = None
- ) -> str:
- return self.dap_server.collect_output(
- "important", timeout=timeout, pattern=pattern
- )
+ def collect_important(self, pattern: Optional[str] = None) -> str:
+ return self.dap_server.collect_output("important", pattern=pattern)
def get_local_as_int(self, name, threadId=None):
value = self.dap_server.get_local_variable_value(name, threadId=threadId)
@@ -393,14 +376,13 @@ class DAPTestCaseBase(TestBase):
targetId=None,
waitForStop=True,
granularity="statement",
- timeout=DEFAULT_TIMEOUT,
):
response = self.dap_server.request_stepIn(
threadId=threadId, targetId=targetId, granularity=granularity
)
self.assertTrue(response["success"])
if waitForStop:
- return self.dap_server.wait_for_stopped(timeout)
+ return self.dap_server.wait_for_stopped()
return None
def stepOver(
@@ -408,7 +390,6 @@ class DAPTestCaseBase(TestBase):
threadId=None,
waitForStop=True,
granularity="statement",
- timeout=DEFAULT_TIMEOUT,
):
response = self.dap_server.request_next(
threadId=threadId, granularity=granularity
@@ -417,40 +398,40 @@ class DAPTestCaseBase(TestBase):
response["success"], f"next request failed: response {response}"
)
if waitForStop:
- return self.dap_server.wait_for_stopped(timeout)
+ return self.dap_server.wait_for_stopped()
return None
- def stepOut(self, threadId=None, waitForStop=True, timeout=DEFAULT_TIMEOUT):
+ def stepOut(self, threadId=None, waitForStop=True):
self.dap_server.request_stepOut(threadId=threadId)
if waitForStop:
- return self.dap_server.wait_for_stopped(timeout)
+ return self.dap_server.wait_for_stopped()
return None
def do_continue(self): # `continue` is a keyword.
resp = self.dap_server.request_continue()
self.assertTrue(resp["success"], f"continue request failed: {resp}")
- def continue_to_next_stop(self, timeout=DEFAULT_TIMEOUT):
+ def continue_to_next_stop(self):
self.do_continue()
- return self.dap_server.wait_for_stopped(timeout)
+ return self.dap_server.wait_for_stopped()
- def continue_to_breakpoint(self, breakpoint_id: str, timeout=DEFAULT_TIMEOUT):
- self.continue_to_breakpoints((breakpoint_id), timeout)
+ def continue_to_breakpoint(self, breakpoint_id: str):
+ self.continue_to_breakpoints((breakpoint_id))
- def continue_to_breakpoints(self, breakpoint_ids, timeout=DEFAULT_TIMEOUT):
+ def continue_to_breakpoints(self, breakpoint_ids):
self.do_continue()
- self.verify_breakpoint_hit(breakpoint_ids, timeout)
+ self.verify_breakpoint_hit(breakpoint_ids)
- def continue_to_exception_breakpoint(self, filter_label, timeout=DEFAULT_TIMEOUT):
+ def continue_to_exception_breakpoint(self, filter_label):
self.do_continue()
self.assertTrue(
- self.verify_stop_exception_info(filter_label, timeout),
+ self.verify_stop_exception_info(filter_label),
'verify we got "%s"' % (filter_label),
)
- def continue_to_exit(self, exitCode=0, timeout=DEFAULT_TIMEOUT):
+ def continue_to_exit(self, exitCode=0):
self.do_continue()
- stopped_events = self.dap_server.wait_for_stopped(timeout)
+ stopped_events = self.dap_server.wait_for_stopped()
self.assertEqual(
len(stopped_events), 1, "stopped_events = {}".format(stopped_events)
)
diff --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index 19c766996292..5ba642bbedf7 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -444,13 +444,20 @@ class GdbRemoteTestCaseBase(Base, metaclass=GdbRemoteTestCaseFactory):
if not exe_path:
exe_path = self.getBuildArtifact("a.out")
- args = []
+ # This file will be created once the inferior has enabled attaching.
+ sync_file_path = lldbutil.append_to_process_working_directory(
+ self, "process_ready"
+ )
+ args = [f"syncfile:{sync_file_path}"]
if inferior_args:
args.extend(inferior_args)
if sleep_seconds:
args.append("sleep:%d" % sleep_seconds)
- return self.spawnSubprocess(exe_path, args)
+ inferior = self.spawnSubprocess(exe_path, args)
+ lldbutil.wait_for_file_on_target(self, sync_file_path)
+
+ return inferior
def prep_debug_monitor_and_inferior(
self,
@@ -924,6 +931,7 @@ class GdbRemoteTestCaseBase(Base, metaclass=GdbRemoteTestCaseFactory):
"QNonStop",
"SupportedWatchpointTypes",
"SupportedCompressions",
+ "MultiMemRead",
]
def parse_qSupported_response(self, context):
diff --git a/lldb/packages/Python/lldbsuite/test_event/build_exception.py b/lldb/packages/Python/lldbsuite/test_event/build_exception.py
index 931c15da0d5e..c3ae2cdfca06 100644
--- a/lldb/packages/Python/lldbsuite/test_event/build_exception.py
+++ b/lldb/packages/Python/lldbsuite/test_event/build_exception.py
@@ -1,10 +1,10 @@
-from lldbsuite.support import seven
+import shlex
class BuildError(Exception):
def __init__(self, called_process_error):
super(BuildError, self).__init__("Error when building test subject")
- self.command = seven.join_for_shell(called_process_error.cmd)
+ self.command = shlex.join(called_process_error.cmd)
self.build_error = called_process_error.output
def __str__(self):
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp
index 07c0a2ea907b..04af7a3aecda 100644
--- a/lldb/source/API/SBBreakpoint.cpp
+++ b/lldb/source/API/SBBreakpoint.cpp
@@ -275,7 +275,11 @@ void SBBreakpoint::SetCondition(const char *condition) {
if (bkpt_sp) {
std::lock_guard<std::recursive_mutex> guard(
bkpt_sp->GetTarget().GetAPIMutex());
- bkpt_sp->SetCondition(StopCondition(condition));
+ // Treat a null pointer as resetting the condition.
+ if (!condition)
+ bkpt_sp->SetCondition(StopCondition());
+ else
+ bkpt_sp->SetCondition(StopCondition(condition));
}
}
@@ -288,7 +292,10 @@ const char *SBBreakpoint::GetCondition() {
std::lock_guard<std::recursive_mutex> guard(
bkpt_sp->GetTarget().GetAPIMutex());
- return ConstString(bkpt_sp->GetCondition().GetText()).GetCString();
+ StopCondition cond = bkpt_sp->GetCondition();
+ if (!cond)
+ return nullptr;
+ return ConstString(cond.GetText()).GetCString();
}
void SBBreakpoint::SetAutoContinue(bool auto_continue) {
@@ -567,6 +574,15 @@ SBError SBBreakpoint::AddLocation(SBAddress &address) {
return error;
}
+SBBreakpointLocation SBBreakpoint::AddFacadeLocation() {
+ BreakpointSP bkpt_sp = GetSP();
+ if (!bkpt_sp)
+ return {};
+
+ BreakpointLocationSP loc_sp = bkpt_sp->AddFacadeLocation();
+ return SBBreakpointLocation(loc_sp);
+}
+
SBStructuredData SBBreakpoint::SerializeToStructuredData() {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp
index e786435c4f8a..2feaa5c805a1 100644
--- a/lldb/source/API/SBBreakpointLocation.cpp
+++ b/lldb/source/API/SBBreakpointLocation.cpp
@@ -160,7 +160,11 @@ void SBBreakpointLocation::SetCondition(const char *condition) {
if (loc_sp) {
std::lock_guard<std::recursive_mutex> guard(
loc_sp->GetTarget().GetAPIMutex());
- loc_sp->SetCondition(StopCondition(condition));
+ // Treat a nullptr as clearing the condition
+ if (!condition)
+ loc_sp->SetCondition(StopCondition());
+ else
+ loc_sp->SetCondition(StopCondition(condition));
}
}
@@ -173,7 +177,10 @@ const char *SBBreakpointLocation::GetCondition() {
std::lock_guard<std::recursive_mutex> guard(
loc_sp->GetTarget().GetAPIMutex());
- return ConstString(loc_sp->GetCondition().GetText()).GetCString();
+ StopCondition cond = loc_sp->GetCondition();
+ if (!cond)
+ return nullptr;
+ return ConstString(cond.GetText()).GetCString();
}
void SBBreakpointLocation::SetAutoContinue(bool auto_continue) {
diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 603e30649784..5c4c653d95a8 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -983,6 +983,17 @@ uint32_t SBDebugger::GetIndexOfTarget(lldb::SBTarget target) {
return m_opaque_sp->GetTargetList().GetIndexOfTarget(target.GetSP());
}
+SBTarget SBDebugger::FindTargetByGloballyUniqueID(lldb::user_id_t id) {
+ LLDB_INSTRUMENT_VA(this, id);
+ SBTarget sb_target;
+ if (m_opaque_sp) {
+ // No need to lock, the target list is thread safe
+ sb_target.SetSP(
+ m_opaque_sp->GetTargetList().FindTargetByGloballyUniqueID(id));
+ }
+ return sb_target;
+}
+
SBTarget SBDebugger::FindTargetWithProcessID(lldb::pid_t pid) {
LLDB_INSTRUMENT_VA(this, pid);
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index eb56337de3c4..98d10aa07c53 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -255,6 +255,7 @@ SBProcess SBTarget::LoadCore(const char *core_file, lldb::SBError &error) {
ProcessSP process_sp(target_sp->CreateProcess(
target_sp->GetDebugger().GetListener(), "", &filespec, false));
if (process_sp) {
+ ElapsedTime load_core_time(target_sp->GetStatistics().GetLoadCoreTime());
error.SetError(process_sp->LoadCore());
if (error.Success())
sb_process.SetSP(process_sp);
@@ -1632,6 +1633,14 @@ const char *SBTarget::GetLabel() const {
return nullptr;
}
+lldb::user_id_t SBTarget::GetGloballyUniqueID() const {
+ LLDB_INSTRUMENT_VA(this);
+
+ if (TargetSP target_sp = GetSP())
+ return target_sp->GetGloballyUniqueID();
+ return LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID;
+}
+
SBError SBTarget::SetLabel(const char *label) {
LLDB_INSTRUMENT_VA(this, label);
diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp
index 1544bf85fb85..b23d1143d60c 100644
--- a/lldb/source/Breakpoint/Breakpoint.cpp
+++ b/lldb/source/Breakpoint/Breakpoint.cpp
@@ -58,7 +58,13 @@ Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp)
m_hit_counter() {}
// Destructor
-Breakpoint::~Breakpoint() = default;
+Breakpoint::~Breakpoint() {
+ for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations())
+ location_sp->SetInvalid();
+ for (BreakpointLocationSP location_sp :
+ m_facade_locations.BreakpointLocations())
+ location_sp->SetInvalid();
+}
BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target,
const Breakpoint &bp_to_copy_from) {
@@ -302,6 +308,20 @@ BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
new_location);
}
+BreakpointLocationSP Breakpoint::AddFacadeLocation() {
+ size_t next_id = m_facade_locations.GetSize() + 1;
+ BreakpointLocationSP break_loc_sp =
+ std::make_shared<BreakpointLocation>(next_id, *this);
+ break_loc_sp->m_is_facade = true;
+ m_facade_locations.Add(break_loc_sp);
+ return break_loc_sp;
+}
+
+BreakpointLocationSP
+Breakpoint::GetFacadeLocationByID(lldb::break_id_t loc_id) {
+ return m_facade_locations.GetByIndex(loc_id - 1);
+}
+
BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) {
return m_locations.FindByAddress(addr);
}
@@ -310,15 +330,23 @@ break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) {
return m_locations.FindIDByAddress(addr);
}
-BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id) {
+BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id,
+ bool use_facade) {
+ if (use_facade && m_facade_locations.GetSize())
+ return GetFacadeLocationByID(bp_loc_id);
return m_locations.FindByID(bp_loc_id);
}
-BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) {
+BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index,
+ bool use_facade) {
+ if (use_facade && m_facade_locations.GetSize() > 0)
+ return m_facade_locations.GetByIndex(index);
return m_locations.GetByIndex(index);
}
void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) {
+ // FIXME: Should we ask the scripted resolver whether any of its facade
+ // locations are invalid?
m_locations.RemoveInvalidLocations(arch);
}
@@ -864,9 +892,15 @@ void Breakpoint::ModuleReplaced(ModuleSP old_module_sp,
void Breakpoint::Dump(Stream *) {}
-size_t Breakpoint::GetNumResolvedLocations() const {
+size_t Breakpoint::GetNumResolvedLocations(bool use_facade) const {
// Return the number of breakpoints that are actually resolved and set down
// in the inferior process.
+ // All facade locations are considered to be resolved:
+ if (use_facade) {
+ size_t num_facade_locs = m_facade_locations.GetSize();
+ if (num_facade_locs)
+ return num_facade_locs;
+ }
return m_locations.GetNumResolvedLocations();
}
@@ -874,7 +908,14 @@ bool Breakpoint::HasResolvedLocations() const {
return GetNumResolvedLocations() > 0;
}
-size_t Breakpoint::GetNumLocations() const { return m_locations.GetSize(); }
+size_t Breakpoint::GetNumLocations(bool use_facade) const {
+ if (use_facade) {
+ size_t num_facade_locs = m_facade_locations.GetSize();
+ if (num_facade_locs > 0)
+ return num_facade_locs;
+ }
+ return m_locations.GetSize();
+}
void Breakpoint::AddName(llvm::StringRef new_name) {
m_name_list.insert(new_name.str());
@@ -899,8 +940,31 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
s->Printf("Kind: %s\n", GetBreakpointKind());
}
- const size_t num_locations = GetNumLocations();
- const size_t num_resolved_locations = GetNumResolvedLocations();
+ bool show_both_types = level == eDescriptionLevelVerbose &&
+ HasFacadeLocations() && show_locations;
+ uint8_t display_mask = eDisplayFacade;
+ if (show_both_types)
+ display_mask |= eDisplayHeader;
+
+ GetDescriptionForType(s, level, display_mask, show_locations);
+
+ if (show_both_types) {
+ display_mask = eDisplayReal | eDisplayHeader;
+ GetDescriptionForType(s, level, display_mask, show_locations);
+ }
+ // Reset the colors back to normal if they were previously greyed out.
+ if (dim_breakpoint_description)
+ s->Printf("%s", ansi::FormatAnsiTerminalCodes(
+ GetTarget().GetDebugger().GetDisabledAnsiSuffix())
+ .c_str());
+}
+
+void Breakpoint::GetDescriptionForType(Stream *s, lldb::DescriptionLevel level,
+ uint8_t display_type,
+ bool show_locations) {
+ bool use_facade = (display_type & eDisplayFacade) != 0;
+ const size_t num_locations = GetNumLocations(use_facade);
+ const size_t num_resolved_locations = GetNumResolvedLocations(use_facade);
// They just made the breakpoint, they don't need to be told HOW they made
// it... Also, we'll print the breakpoint number differently depending on
@@ -957,7 +1021,7 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
} else if (num_locations == 1 && !show_locations) {
// There is only one location, so we'll just print that location
// information.
- GetLocationAtIndex(0)->GetDescription(s, level);
+ GetLocationAtIndex(0, use_facade)->GetDescription(s, level);
} else {
s->Printf("%" PRIu64 " locations.", static_cast<uint64_t>(num_locations));
}
@@ -979,20 +1043,20 @@ void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level,
// The brief description is just the location name (1.2 or whatever). That's
// pointless to show in the breakpoint's description, so suppress it.
if (show_locations && level != lldb::eDescriptionLevelBrief) {
+ if ((display_type & eDisplayHeader) != 0) {
+ if ((display_type & eDisplayFacade) != 0)
+ s->Printf("Facade locations:\n");
+ else
+ s->Printf("Implementation Locations\n");
+ }
s->IndentMore();
for (size_t i = 0; i < num_locations; ++i) {
- BreakpointLocation *loc = GetLocationAtIndex(i).get();
+ BreakpointLocation *loc = GetLocationAtIndex(i, use_facade).get();
loc->GetDescription(s, level);
s->EOL();
}
s->IndentLess();
}
-
- // Reset the colors back to normal if they were previously greyed out.
- if (dim_breakpoint_description)
- s->Printf("%s", ansi::FormatAnsiTerminalCodes(
- GetTarget().GetDebugger().GetDisabledAnsiSuffix())
- .c_str());
}
void Breakpoint::GetResolverDescription(Stream *s) {
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index 443d4f50833d..22c98acda8c5 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -8,6 +8,8 @@
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
@@ -45,6 +47,13 @@ BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner,
SetThreadIDInternal(tid);
}
+BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner)
+ : m_should_resolve_indirect_functions(false), m_is_reexported(false),
+ m_is_indirect(false), m_address(LLDB_INVALID_ADDRESS), m_owner(owner),
+ m_condition_hash(0), m_loc_id(loc_id), m_hit_counter() {
+ SetThreadIDInternal(LLDB_INVALID_THREAD_ID);
+}
+
BreakpointLocation::~BreakpointLocation() {
llvm::consumeError(ClearBreakpointSite());
}
@@ -372,12 +381,43 @@ bool BreakpointLocation::ValidForThisThread(Thread &thread) {
.GetThreadSpecNoCreate());
}
+BreakpointLocationSP
+BreakpointLocation::WasHit(StoppointCallbackContext *context) {
+ // Only the BreakpointResolverScripted provides WasHit.
+ BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver();
+ BreakpointResolverScripted *scripted =
+ llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get());
+ if (!scripted)
+ return shared_from_this();
+
+ StackFrameSP frame_sp = context->exe_ctx_ref.GetFrameSP();
+ if (!frame_sp)
+ return shared_from_this();
+
+ BreakpointLocationSP return_loc_sp =
+ scripted->WasHit(frame_sp, shared_from_this());
+ // If this is a facade location, then we won't have bumped its hit count
+ // while processing the original location hit. Do so here. We don't need
+ // to bump the breakpoint's hit count, however, since hitting the real
+ // location would have already done that.
+ // Also we have to check the enabled state here, since we would never have
+ // gotten here with a real location...
+ if (return_loc_sp && return_loc_sp->IsFacade()) {
+ if (return_loc_sp->IsEnabled())
+ return_loc_sp->m_hit_counter.Increment();
+ else
+ return {};
+ }
+ return return_loc_sp;
+}
+
// RETURNS - true if we should stop at this breakpoint, false if we
// should continue. Note, we don't check the thread spec for the breakpoint
// here, since if the breakpoint is not for this thread, then the event won't
// even get reported, so the check is redundant.
-bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
+bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context,
+ lldb::BreakpointLocationSP &facade_loc_sp) {
bool should_stop = true;
Log *log = GetLog(LLDBLog::Breakpoints);
@@ -386,6 +426,27 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
if (!IsEnabled())
return false;
+ // Next check WasHit:
+ BreakpointLocationSP loc_hit_sp = WasHit(context);
+
+ if (!loc_hit_sp) {
+ // We bump the hit counts in StopInfoBreakpoint::ShouldStopSynchronous,
+ // before we call into each location's ShouldStop. So we need to undo
+ // that here.
+ UndoBumpHitCount();
+ return false;
+ }
+
+ // If the location hit was not us, it was a facade location, in which case
+ // we should use the facade location's callbacks, etc. Those will all be
+ // run in the asynchronous phase, so for now we just have to record the fact
+ // that we should treat this as a facade hit. This is strictly an out
+ // parameter, so clear it if this isn't a facade hit.
+ if (loc_hit_sp.get() != this)
+ facade_loc_sp = loc_hit_sp;
+ else
+ facade_loc_sp.reset();
+
// We only run synchronous callbacks in ShouldStop:
context->is_synchronous = true;
should_stop = InvokeCallback(context);
@@ -395,6 +456,11 @@ bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
GetDescription(&s, lldb::eDescriptionLevelVerbose);
LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(),
should_stop ? "stopping" : "continuing");
+ if (facade_loc_sp) {
+ s.Clear();
+ facade_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ LLDB_LOGF(log, "Attributing to facade location: %s.\n", s.GetData());
+ }
}
return should_stop;
@@ -417,7 +483,10 @@ void BreakpointLocation::UndoBumpHitCount() {
}
bool BreakpointLocation::IsResolved() const {
- return m_bp_site_sp.get() != nullptr;
+
+ bool has_site = m_bp_site_sp.get() != nullptr;
+ // Facade locations are currently always considered resolved.
+ return has_site || IsFacade();
}
lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
@@ -425,7 +494,9 @@ lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
}
llvm::Error BreakpointLocation::ResolveBreakpointSite() {
- if (m_bp_site_sp)
+ // This might be a facade location, which doesn't have an address.
+ // In that case, don't attempt to make a site.
+ if (m_bp_site_sp || IsFacade())
return llvm::Error::success();
Process *process = m_owner.GetTarget().GetProcessSP().get();
@@ -454,8 +525,12 @@ bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) {
}
llvm::Error BreakpointLocation::ClearBreakpointSite() {
- if (!m_bp_site_sp)
+ if (!m_bp_site_sp) {
+ // This might be a Facade Location, which don't have sites or addresses
+ if (IsFacade())
+ return llvm::Error::success();
return llvm::createStringError("no breakpoint site to clear");
+ }
// If the process exists, get it to remove the owner, it will remove the
// physical implementation of the breakpoint as well if there are no more
@@ -474,6 +549,17 @@ void BreakpointLocation::GetDescription(Stream *s,
lldb::DescriptionLevel level) {
SymbolContext sc;
+ // If this is a scripted breakpoint, give it a chance to describe its
+ // locations:
+ std::optional<std::string> scripted_opt;
+ BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver();
+ BreakpointResolverScripted *scripted =
+ llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get());
+ if (scripted)
+ scripted_opt = scripted->GetLocationDescription(shared_from_this(), level);
+
+ bool is_scripted_desc = scripted_opt.has_value();
+
// If the description level is "initial" then the breakpoint is printing out
// our initial state, and we should let it decide how it wants to print our
// label.
@@ -491,7 +577,9 @@ void BreakpointLocation::GetDescription(Stream *s,
if (level == lldb::eDescriptionLevelVerbose)
s->IndentMore();
- if (m_address.IsSectionOffset()) {
+ if (is_scripted_desc) {
+ s->PutCString(scripted_opt->c_str());
+ } else if (m_address.IsSectionOffset()) {
m_address.CalculateSymbolContext(&sc);
if (level == lldb::eDescriptionLevelFull ||
@@ -566,43 +654,51 @@ void BreakpointLocation::GetDescription(Stream *s,
s->Indent();
}
- if (m_address.IsSectionOffset() &&
- (level == eDescriptionLevelFull || level == eDescriptionLevelInitial))
- s->Printf(", ");
- s->Printf("address = ");
-
- ExecutionContextScope *exe_scope = nullptr;
- Target *target = &m_owner.GetTarget();
- if (target)
- exe_scope = target->GetProcessSP().get();
- if (exe_scope == nullptr)
- exe_scope = target;
-
- if (level == eDescriptionLevelInitial)
- m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
- Address::DumpStyleFileAddress);
- else
- m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
- Address::DumpStyleModuleWithFileAddress);
-
- if (IsIndirect() && m_bp_site_sp) {
- Address resolved_address;
- resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);
- Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
- if (resolved_symbol) {
- if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)
- s->Printf(", ");
- else if (level == lldb::eDescriptionLevelVerbose) {
- s->EOL();
- s->Indent();
+ if (!is_scripted_desc) {
+ if (m_address.IsSectionOffset() &&
+ (level == eDescriptionLevelFull || level == eDescriptionLevelInitial))
+ s->Printf(", ");
+ s->Printf("address = ");
+
+ ExecutionContextScope *exe_scope = nullptr;
+ Target *target = &m_owner.GetTarget();
+ if (target)
+ exe_scope = target->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target;
+
+ if (level == eDescriptionLevelInitial)
+ m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ else
+ m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress);
+
+ if (IsIndirect() && m_bp_site_sp) {
+ Address resolved_address;
+ resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);
+ Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
+ if (resolved_symbol) {
+ if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)
+ s->Printf(", ");
+ else if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->Indent();
+ }
+ s->Printf("indirect target = %s",
+ resolved_symbol->GetName().GetCString());
}
- s->Printf("indirect target = %s",
- resolved_symbol->GetName().GetCString());
}
}
- bool is_resolved = IsResolved();
- bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();
+ // FIXME: scripted breakpoint are currently always resolved. Does this seem
+ // right? If they don't add any scripted locations, we shouldn't consider them
+ // resolved.
+ bool is_resolved = is_scripted_desc || IsResolved();
+ // A scripted breakpoint might be resolved but not have a site. Be sure to
+ // check for that.
+ bool is_hardware = !is_scripted_desc && IsResolved() && m_bp_site_sp &&
+ m_bp_site_sp->IsHardware();
if (level == lldb::eDescriptionLevelVerbose) {
s->EOL();
@@ -717,9 +813,9 @@ void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
}
void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) {
- if (thread_id != LLDB_INVALID_THREAD_ID)
+ if (thread_id != LLDB_INVALID_THREAD_ID) {
GetLocationOptions().SetThreadID(thread_id);
- else {
+ } else {
// If we're resetting this to an invalid thread id, then don't make an
// options pointer just to do that.
if (m_options_up != nullptr)
diff --git a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
index 81bec0bd7583..1d052c5fc9bb 100644
--- a/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
@@ -115,7 +115,8 @@ BreakpointLocationCollection::GetByIndex(size_t i) const {
}
bool BreakpointLocationCollection::ShouldStop(
- StoppointCallbackContext *context) {
+ StoppointCallbackContext *context,
+ BreakpointLocationCollection &stopped_bp_locs) {
bool shouldStop = false;
size_t i = 0;
size_t prev_size = GetSize();
@@ -123,9 +124,20 @@ bool BreakpointLocationCollection::ShouldStop(
// ShouldStop can remove the breakpoint from the list, or even delete
// it, so we should
BreakpointLocationSP cur_loc_sp = GetByIndex(i);
+ BreakpointLocationSP reported_loc_sp;
BreakpointSP keep_bkpt_alive_sp = cur_loc_sp->GetBreakpoint().shared_from_this();
- if (cur_loc_sp->ShouldStop(context))
+ // We're building up the list or which locations claim responsibility for
+ // this stop. If the location's ShouldStop defers to a facade location by
+ // returning a non-null reported location, we want to use that. Otherwise
+ // use the original location.
+ if (cur_loc_sp->ShouldStop(context, reported_loc_sp)) {
+ if (reported_loc_sp)
+ stopped_bp_locs.Add(reported_loc_sp);
+ else
+ stopped_bp_locs.Add(cur_loc_sp);
+
shouldStop = true;
+ }
if (prev_size == GetSize())
i++;
diff --git a/lldb/source/Breakpoint/BreakpointLocationList.cpp b/lldb/source/Breakpoint/BreakpointLocationList.cpp
index 44d1eb5bf714..ea431aa3bb95 100644
--- a/lldb/source/Breakpoint/BreakpointLocationList.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocationList.cpp
@@ -41,13 +41,14 @@ BreakpointLocationList::Create(const Address &addr,
}
bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context,
- lldb::break_id_t break_id) {
+ lldb::break_id_t break_id,
+ lldb::BreakpointLocationSP &bp_loc_sp) {
BreakpointLocationSP bp = FindByID(break_id);
if (bp) {
// Let the BreakpointLocation decide if it should stop here (could not have
// reached it's target hit count yet, or it could have a callback that
// decided it shouldn't stop (shared library loads/unloads).
- return bp->ShouldStop(context);
+ return bp->ShouldStop(context, bp_loc_sp);
}
// We should stop here since this BreakpointLocation isn't valid anymore or
// it doesn't exist.
diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp
index 6372595a0f21..4f252f91cccd 100644
--- a/lldb/source/Breakpoint/BreakpointResolverName.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp
@@ -233,7 +233,7 @@ void BreakpointResolverName::AddNameLookup(ConstString name,
m_lookups.emplace_back(variant_lookup);
}
}
- return true;
+ return IterationAction::Continue;
};
if (Language *lang = Language::FindPlugin(m_language)) {
diff --git a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
index 701cabae3ee9..373bd74d24b6 100644
--- a/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
+++ b/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
@@ -50,7 +50,9 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded(
if (!script_interp)
return;
- m_interface_sp = script_interp->CreateScriptedBreakpointInterface();
+ if (!m_interface_sp)
+ m_interface_sp = script_interp->CreateScriptedBreakpointInterface();
+
if (!m_interface_sp) {
m_error = Status::FromErrorStringWithFormat(
"BreakpointResolverScripted::%s () - ERROR: %s", __FUNCTION__,
@@ -61,6 +63,7 @@ void BreakpointResolverScripted::CreateImplementationIfNeeded(
auto obj_or_err =
m_interface_sp->CreatePluginObject(m_class_name, breakpoint_sp, m_args);
if (!obj_or_err) {
+ m_interface_sp.reset();
m_error = Status::FromError(obj_or_err.takeError());
return;
}
@@ -146,6 +149,8 @@ void BreakpointResolverScripted::GetDescription(Stream *s) {
StructuredData::GenericSP generic_sp;
std::optional<std::string> short_help;
+ CreateImplementationIfNeeded(GetBreakpoint());
+
if (m_interface_sp) {
short_help = m_interface_sp->GetShortHelp();
}
@@ -155,6 +160,22 @@ void BreakpointResolverScripted::GetDescription(Stream *s) {
s->Printf("python class = %s", m_class_name.c_str());
}
+std::optional<std::string> BreakpointResolverScripted::GetLocationDescription(
+ lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) {
+ CreateImplementationIfNeeded(GetBreakpoint());
+ if (m_interface_sp)
+ return m_interface_sp->GetLocationDescription(bp_loc_sp, level);
+ return {};
+}
+
+lldb::BreakpointLocationSP
+BreakpointResolverScripted::WasHit(lldb::StackFrameSP frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp) {
+ if (m_interface_sp)
+ return m_interface_sp->WasHit(frame_sp, bp_loc_sp);
+ return {};
+}
+
void BreakpointResolverScripted::Dump(Stream *s) const {}
lldb::BreakpointResolverSP
diff --git a/lldb/source/Breakpoint/BreakpointSite.cpp b/lldb/source/Breakpoint/BreakpointSite.cpp
index d430e3de788f..fd7666be6b1b 100644
--- a/lldb/source/Breakpoint/BreakpointSite.cpp
+++ b/lldb/source/Breakpoint/BreakpointSite.cpp
@@ -45,7 +45,9 @@ break_id_t BreakpointSite::GetNextID() {
// RETURNS - true if we should stop at this breakpoint, false if we
// should continue.
-bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
+bool BreakpointSite::ShouldStop(
+ StoppointCallbackContext *context,
+ BreakpointLocationCollection &stopping_bp_locs) {
m_hit_counter.Increment();
// ShouldStop can do a lot of work, and might even come back and hit
// this breakpoint site again. So don't hold the m_constituents_mutex the
@@ -56,7 +58,7 @@ bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
constituents_copy = m_constituents;
}
- return constituents_copy.ShouldStop(context);
+ return constituents_copy.ShouldStop(context, stopping_bp_locs);
}
bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) {
diff --git a/lldb/source/Commands/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp
index c5ab9e9f05be..1a950899ea1c 100644
--- a/lldb/source/Commands/CommandObjectProtocolServer.cpp
+++ b/lldb/source/Commands/CommandObjectProtocolServer.cpp
@@ -131,15 +131,57 @@ protected:
}
};
+class CommandObjectProtocolServerGet : public CommandObjectParsed {
+public:
+ CommandObjectProtocolServerGet(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "protocol-server get",
+ "get protocol server connection information",
+ "protocol-server get <protocol>") {
+ AddSimpleArgumentList(lldb::eArgTypeProtocol, eArgRepeatPlain);
+ }
+
+ ~CommandObjectProtocolServerGet() override = default;
+
+protected:
+ void DoExecute(Args &args, CommandReturnObject &result) override {
+ if (args.GetArgumentCount() < 1) {
+ result.AppendError("no protocol specified");
+ return;
+ }
+
+ llvm::StringRef protocol = args.GetArgumentAtIndex(0);
+ ProtocolServer *server = ProtocolServer::GetOrCreate(protocol);
+ if (!server) {
+ result.AppendErrorWithFormatv(
+ "unsupported protocol: {0}. Supported protocols are: {1}", protocol,
+ llvm::join(ProtocolServer::GetSupportedProtocols(), ", "));
+ return;
+ }
+
+ Socket *socket = server->GetSocket();
+ if (!socket) {
+ result.AppendErrorWithFormatv("{0} server is not running", protocol);
+ return;
+ }
+
+ std::string address = llvm::join(socket->GetListeningConnectionURI(), ", ");
+ result.AppendMessageWithFormatv("{0} server connection listeners: {1}",
+ protocol, address);
+ result.SetStatus(eReturnStatusSuccessFinishNoResult);
+ }
+};
+
CommandObjectProtocolServer::CommandObjectProtocolServer(
CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "protocol-server",
- "Start and stop a protocol server.",
+ "Start, stop, and query protocol servers.",
"protocol-server") {
LoadSubCommand("start", CommandObjectSP(new CommandObjectProtocolServerStart(
interpreter)));
LoadSubCommand("stop", CommandObjectSP(
new CommandObjectProtocolServerStop(interpreter)));
+ LoadSubCommand(
+ "get", CommandObjectSP(new CommandObjectProtocolServerGet(interpreter)));
}
CommandObjectProtocolServer::~CommandObjectProtocolServer() = default;
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index 940be42d1b6e..c59d02812f32 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -418,7 +418,11 @@ protected:
if (process_sp) {
// Seems weird that we Launch a core file, but that is what we
// do!
- error = process_sp->LoadCore();
+ {
+ ElapsedTime load_core_time(
+ target_sp->GetStatistics().GetLoadCoreTime());
+ error = process_sp->LoadCore();
+ }
if (error.Fail()) {
result.AppendError(error.AsCString("unknown core file format"));
diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp
index 19cd3ff2972e..22ed5b8ed593 100644
--- a/lldb/source/Commands/CommandObjectType.cpp
+++ b/lldb/source/Commands/CommandObjectType.cpp
@@ -2610,7 +2610,7 @@ public:
Language::ForEach([&](Language *lang) {
if (const char *help = lang->GetLanguageSpecificTypeLookupHelp())
stream.Printf("%s\n", help);
- return true;
+ return IterationAction::Continue;
});
m_cmd_help_long = std::string(stream.GetString());
@@ -2649,7 +2649,7 @@ public:
(m_command_options.m_language == eLanguageTypeUnknown))) {
Language::ForEach([&](Language *lang) {
languages.push_back(lang);
- return true;
+ return IterationAction::Continue;
});
} else {
languages.push_back(Language::FindPlugin(m_command_options.m_language));
diff --git a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
index 21d94d68ceb9..8ae20bd76f45 100644
--- a/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
+++ b/lldb/source/Commands/CommandOptionsProcessLaunch.cpp
@@ -127,6 +127,10 @@ Status CommandOptionsProcessLaunch::SetOptionValue(
break;
}
+ case 'M':
+ launch_info.GetFlags().Set(eLaunchFlagMemoryTagging);
+ break;
+
case 'c':
if (!option_arg.empty())
launch_info.SetShell(FileSpec(option_arg));
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 595b3d08abec..a9f054e1d3d4 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1173,6 +1173,11 @@ let Command = "process launch" in {
Arg<"Boolean">,
Desc<"Set whether to shell expand arguments to the process when "
"launching.">;
+ def process_launch_memory_tagging
+ : Option<"memory-tagging", "M">,
+ Desc<"Set whether to explicitly enable memory tagging when launching "
+ "the process. Requires hardware support. "
+ "(Only supported on Darwin.)">;
}
let Command = "process attach" in {
diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td
index fda34a8ad263..1be911c29170 100644
--- a/lldb/source/Core/CoreProperties.td
+++ b/lldb/source/Core/CoreProperties.td
@@ -162,10 +162,12 @@ let Definition = "debugger" in {
Global,
DefaultTrue,
Desc<"Whether to use Ansi color codes or not.">;
- def ShowProgress: Property<"show-progress", "Boolean">,
- Global,
- DefaultTrue,
- Desc<"Whether to show progress or not if the debugger's output is an interactive color-enabled terminal.">;
+ def ShowProgress
+ : Property<"show-progress", "Boolean">,
+ Global,
+ DefaultFalse,
+ Desc<"Whether to show progress using Operating System Command (OSC) "
+ "Sequences in supporting terminal emulators.">;
def ShowProgressAnsiPrefix: Property<"show-progress-ansi-prefix", "String">,
Global,
DefaultStringValue<"${ansi.faint}">,
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index 568cd9d3d03b..b37d9d3ed85e 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -2066,19 +2066,23 @@ void Debugger::CancelForwardEvents(const ListenerSP &listener_sp) {
m_forward_listener_sp.reset();
}
+bool Debugger::IsEscapeCodeCapableTTY() {
+ if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) {
+ File &file = stream_sp->GetUnlockedFile();
+ return file.GetIsInteractive() && file.GetIsRealTerminal() &&
+ file.GetIsTerminalWithColors();
+ }
+ return false;
+}
+
bool Debugger::StatuslineSupported() {
// We have trouble with the contol codes on Windows, see
// https://github.com/llvm/llvm-project/issues/134846.
#ifndef _WIN32
- if (GetShowStatusline()) {
- if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) {
- File &file = stream_sp->GetUnlockedFile();
- return file.GetIsInteractive() && file.GetIsRealTerminal() &&
- file.GetIsTerminalWithColors();
- }
- }
-#endif
+ return GetShowStatusline() && IsEscapeCodeCapableTTY();
+#else
return false;
+#endif
}
static bool RequiresFollowChildWorkaround(const Process &process) {
@@ -2271,10 +2275,11 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
ProgressReport progress_report{data->GetID(), data->GetCompleted(),
data->GetTotal(), data->GetMessage()};
- // Do some bookkeeping regardless of whether we're going to display
- // progress reports.
{
std::lock_guard<std::mutex> guard(m_progress_reports_mutex);
+
+ // Do some bookkeeping regardless of whether we're going to display
+ // progress reports.
auto it = llvm::find_if(m_progress_reports, [&](const auto &report) {
return report.id == progress_report.id;
});
@@ -2287,6 +2292,30 @@ void Debugger::HandleProgressEvent(const lldb::EventSP &event_sp) {
} else {
m_progress_reports.push_back(progress_report);
}
+
+ // Show progress using Operating System Command (OSC) sequences.
+ if (GetShowProgress() && IsEscapeCodeCapableTTY()) {
+ if (lldb::LockableStreamFileSP stream_sp = GetOutputStreamSP()) {
+
+ // Clear progress if this was the last progress event.
+ if (m_progress_reports.empty()) {
+ stream_sp->Lock() << OSC_PROGRESS_REMOVE;
+ return;
+ }
+
+ const ProgressReport &report = m_progress_reports.back();
+
+ // Show indeterminate progress.
+ if (report.total == UINT64_MAX) {
+ stream_sp->Lock() << OSC_PROGRESS_INDETERMINATE;
+ return;
+ }
+
+ // Compute and show the progress value (0-100).
+ const unsigned value = (report.completed / report.total) * 100;
+ stream_sp->Lock().Printf(OSC_PROGRESS_SHOW, value);
+ }
+ }
}
}
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index 57819eeade6e..c2530aa0d00c 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -152,15 +152,16 @@ void IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler,
void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
std::string &line) {
- if (line.empty()) {
+ const llvm::StringRef input = llvm::StringRef(line).rtrim();
+ if (input.empty()) {
// User just hit enter, set the response to the default
m_user_response = m_default_response;
io_handler.SetIsDone(true);
return;
}
- if (line.size() == 1) {
- switch (line[0]) {
+ if (input.size() == 1) {
+ switch (input[0]) {
case 'y':
case 'Y':
m_user_response = true;
@@ -176,10 +177,10 @@ void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler,
}
}
- if (line == "yes" || line == "YES" || line == "Yes") {
+ if (input.equals_insensitive("yes")) {
m_user_response = true;
io_handler.SetIsDone(true);
- } else if (line == "no" || line == "NO" || line == "No") {
+ } else if (input.equals_insensitive("no")) {
m_user_response = false;
io_handler.SetIsDone(true);
}
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index 91b9c0007617..f7683c55baf8 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -40,7 +40,7 @@ bool Mangled::IsMangledName(llvm::StringRef name) {
return Mangled::GetManglingScheme(name) != Mangled::eManglingSchemeNone;
}
-Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef const name) {
+Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef name) {
if (name.empty())
return Mangled::eManglingSchemeNone;
@@ -428,9 +428,9 @@ lldb::LanguageType Mangled::GuessLanguage() const {
Language::ForEach([this, &result](Language *l) {
if (l->SymbolNameFitsToLanguage(*this)) {
result = l->GetLanguageType();
- return false;
+ return IterationAction::Stop;
}
- return true;
+ return IterationAction::Continue;
});
return result;
}
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index bc63a41c90d1..c40612c1ced5 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -349,17 +349,20 @@ bool ModuleList::ReplaceModule(const lldb::ModuleSP &old_module_sp,
return true;
}
-bool ModuleList::RemoveIfOrphaned(const Module *module_ptr) {
- if (module_ptr) {
+bool ModuleList::RemoveIfOrphaned(const ModuleWP module_wp) {
+ if (auto module_sp = module_wp.lock()) {
std::lock_guard<std::recursive_mutex> guard(m_modules_mutex);
collection::iterator pos, end = m_modules.end();
for (pos = m_modules.begin(); pos != end; ++pos) {
- if (pos->get() == module_ptr) {
- if (pos->use_count() == 1) {
+ if (pos->get() == module_sp.get()) {
+ // Since module_sp increases the refcount by 1, the use count should be
+ // the regular use count + 1.
+ constexpr long kUseCountOrphaned = kUseCountModuleListOrphaned + 1;
+ if (pos->use_count() == kUseCountOrphaned) {
pos = RemoveImpl(pos);
return true;
- } else
- return false;
+ }
+ return false;
}
}
}
@@ -386,7 +389,7 @@ size_t ModuleList::RemoveOrphans(bool mandatory) {
made_progress = false;
collection::iterator pos = m_modules.begin();
while (pos != m_modules.end()) {
- if (pos->use_count() == 1) {
+ if (pos->use_count() == kUseCountModuleListOrphaned) {
pos = RemoveImpl(pos);
++remove_count;
// We did make progress.
@@ -832,7 +835,7 @@ public:
if (!module_sp)
return false;
std::lock_guard<std::recursive_mutex> guard(GetMutex());
- RemoveFromMap(*module_sp.get());
+ RemoveFromMap(module_sp);
return m_list.Remove(module_sp, use_notifier);
}
@@ -843,10 +846,10 @@ public:
ReplaceEquivalentInMap(module_sp);
}
- bool RemoveIfOrphaned(const Module *module_ptr) {
+ bool RemoveIfOrphaned(const ModuleWP module_wp) {
std::lock_guard<std::recursive_mutex> guard(GetMutex());
- RemoveFromMap(*module_ptr, /*if_orphaned=*/true);
- return m_list.RemoveIfOrphaned(module_ptr);
+ RemoveFromMap(module_wp, /*if_orphaned=*/true);
+ return m_list.RemoveIfOrphaned(module_wp);
}
std::recursive_mutex &GetMutex() const { return m_list.GetMutex(); }
@@ -886,16 +889,22 @@ private:
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 RemoveFromMap(const ModuleWP module_wp, bool if_orphaned = false) {
+ if (auto module_sp = module_wp.lock()) {
+ ConstString name = module_sp->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_sp.get()) {
+ // Since module_sp increases the refcount by 1, the use count should
+ // be the regular use count + 1.
+ constexpr long kUseCountOrphaned =
+ kUseCountSharedModuleListOrphaned + 1;
+ if (!if_orphaned || it->use_count() == kUseCountOrphaned) {
+ vec.erase(it);
+ break;
+ }
}
}
}
@@ -933,7 +942,7 @@ private:
// 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;
+ return module.use_count() == kUseCountSharedModuleListOrphaned;
});
ModuleList to_remove;
@@ -976,11 +985,11 @@ private:
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;
+ static constexpr long kUseCountSharedModuleListOrphaned = 2;
};
struct SharedModuleListInfo {
- ModuleList module_list;
+ SharedModuleList module_list;
ModuleListProperties module_list_properties;
};
}
@@ -998,7 +1007,7 @@ static SharedModuleListInfo &GetSharedModuleListInfo()
return *g_shared_module_list_info;
}
-static ModuleList &GetSharedModuleList() {
+static SharedModuleList &GetSharedModuleList() {
return GetSharedModuleListInfo().module_list;
}
@@ -1008,8 +1017,8 @@ ModuleListProperties &ModuleList::GetGlobalModuleListProperties() {
bool ModuleList::ModuleIsInCache(const Module *module_ptr) {
if (module_ptr) {
- ModuleList &shared_module_list = GetSharedModuleList();
- return shared_module_list.FindModule(module_ptr).get() != nullptr;
+ SharedModuleList &shared_module_list = GetSharedModuleList();
+ return shared_module_list.FindModule(*module_ptr).get() != nullptr;
}
return false;
}
@@ -1032,9 +1041,8 @@ ModuleList::GetSharedModule(const ModuleSpec &module_spec, ModuleSP &module_sp,
const FileSpecList *module_search_paths_ptr,
llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
bool *did_create_ptr, bool always_create) {
- ModuleList &shared_module_list = GetSharedModuleList();
- std::lock_guard<std::recursive_mutex> guard(
- shared_module_list.m_modules_mutex);
+ SharedModuleList &shared_module_list = GetSharedModuleList();
+ std::lock_guard<std::recursive_mutex> guard(shared_module_list.GetMutex());
char path[PATH_MAX];
Status error;
@@ -1278,8 +1286,8 @@ bool ModuleList::RemoveSharedModule(lldb::ModuleSP &module_sp) {
return GetSharedModuleList().Remove(module_sp);
}
-bool ModuleList::RemoveSharedModuleIfOrphaned(const Module *module_ptr) {
- return GetSharedModuleList().RemoveIfOrphaned(module_ptr);
+bool ModuleList::RemoveSharedModuleIfOrphaned(const ModuleWP module_wp) {
+ return GetSharedModuleList().RemoveIfOrphaned(module_wp);
}
bool ModuleList::LoadScriptingResourcesInTarget(Target *target,
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 25d4a87b89ef..60b9de0d21b2 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -751,7 +751,12 @@ ResolveFunctionCallLabel(FunctionCallLabel &label,
sc_list.Append(*sc_or_err);
LoadAddressResolver resolver(*sc.target_sp, symbol_was_missing_weak);
- return resolver.Resolve(sc_list).value_or(LLDB_INVALID_ADDRESS);
+ lldb::addr_t resolved_addr =
+ resolver.Resolve(sc_list).value_or(LLDB_INVALID_ADDRESS);
+ if (resolved_addr == LLDB_INVALID_ADDRESS)
+ return llvm::createStringError("couldn't resolve address for function");
+
+ return resolved_addr;
}
lldb::addr_t
diff --git a/lldb/source/Host/common/JSONTransport.cpp b/lldb/source/Host/common/JSONTransport.cpp
index c4b42eafc85d..22de7fa8cbea 100644
--- a/lldb/source/Host/common/JSONTransport.cpp
+++ b/lldb/source/Host/common/JSONTransport.cpp
@@ -14,8 +14,7 @@
#include <string>
using namespace llvm;
-using namespace lldb;
-using namespace lldb_private;
+using namespace lldb_private::transport;
char TransportUnhandledContentsError::ID;
@@ -23,10 +22,31 @@ TransportUnhandledContentsError::TransportUnhandledContentsError(
std::string unhandled_contents)
: m_unhandled_contents(unhandled_contents) {}
-void TransportUnhandledContentsError::log(llvm::raw_ostream &OS) const {
+void TransportUnhandledContentsError::log(raw_ostream &OS) const {
OS << "transport EOF with unhandled contents: '" << m_unhandled_contents
<< "'";
}
std::error_code TransportUnhandledContentsError::convertToErrorCode() const {
return std::make_error_code(std::errc::bad_message);
}
+
+char InvalidParams::ID;
+
+void InvalidParams::log(raw_ostream &OS) const {
+ OS << "invalid parameters for method '" << m_method << "': '" << m_context
+ << "'";
+}
+std::error_code InvalidParams::convertToErrorCode() const {
+ return std::make_error_code(std::errc::invalid_argument);
+}
+
+char MethodNotFound::ID;
+
+void MethodNotFound::log(raw_ostream &OS) const {
+ OS << "method not found: '" << m_method << "'";
+}
+
+std::error_code MethodNotFound::convertToErrorCode() const {
+ // JSON-RPC Method not found
+ return std::error_code(MethodNotFound::kErrorCode, std::generic_category());
+}
diff --git a/lldb/source/Host/freebsd/Host.cpp b/lldb/source/Host/freebsd/Host.cpp
index 14c0e9f2209d..dfdbfea0c3c0 100644
--- a/lldb/source/Host/freebsd/Host.cpp
+++ b/lldb/source/Host/freebsd/Host.cpp
@@ -14,12 +14,11 @@
#include <sys/sysctl.h>
#include <sys/user.h>
-#include <machine/elf.h>
-
#include <cstdio>
#include <dlfcn.h>
#include <execinfo.h>
+#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/DataBufferHeap.h"
@@ -31,6 +30,7 @@
#include "lldb/Utility/Status.h"
#include "lldb/Utility/StreamString.h"
+#include "llvm/Object/ELF.h"
#include "llvm/TargetParser/Host.h"
namespace lldb_private {
@@ -97,17 +97,33 @@ GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
proc_args.AppendArgument(llvm::StringRef(cstr));
}
- return true;
-}
-
-static bool GetFreeBSDProcessCPUType(ProcessInstanceInfo &process_info) {
- if (process_info.ProcessIDIsValid()) {
- process_info.GetArchitecture() =
- HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
+ auto buffer_sp = FileSystem::Instance().CreateDataBuffer(pathname, 0x20, 0);
+ if (!buffer_sp) {
+ process_info.Clear();
return true;
}
- process_info.GetArchitecture().Clear();
- return false;
+ uint8_t exe_class =
+ llvm::object::getElfArchType(
+ {reinterpret_cast<const char *>(buffer_sp->GetBytes()),
+ size_t(buffer_sp->GetByteSize())})
+ .first;
+
+ switch (exe_class) {
+ case llvm::ELF::ELFCLASS32:
+ process_info.SetArchitecture(
+ HostInfo::GetArchitecture(HostInfo::eArchKind32));
+ break;
+ case llvm::ELF::ELFCLASS64:
+ process_info.SetArchitecture(
+ HostInfo::GetArchitecture(HostInfo::eArchKind64));
+ break;
+ case llvm::ELF::ELFCLASSNONE:
+ process_info.SetArchitecture(
+ HostInfo::GetArchitecture(HostInfo::eArchKindDefault));
+ break;
+ }
+
+ return true;
}
static bool GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
@@ -214,7 +230,6 @@ uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
// Make sure our info matches before we go fetch the name and cpu type
if (match_info_noname.Matches(process_info) &&
GetFreeBSDProcessArgs(&match_info, process_info)) {
- GetFreeBSDProcessCPUType(process_info);
if (match_info.Matches(process_info))
process_infos.push_back(process_info);
}
@@ -228,7 +243,6 @@ bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
if (GetFreeBSDProcessArgs(NULL, process_info)) {
// should use libprocstat instead of going right into sysctl?
- GetFreeBSDProcessCPUType(process_info);
GetFreeBSDProcessUserAndGroup(process_info);
return true;
}
diff --git a/lldb/source/Host/macosx/objcxx/Host.mm b/lldb/source/Host/macosx/objcxx/Host.mm
index 3c1d1179963d..96a282c64e44 100644
--- a/lldb/source/Host/macosx/objcxx/Host.mm
+++ b/lldb/source/Host/macosx/objcxx/Host.mm
@@ -1210,6 +1210,38 @@ static Status LaunchProcessPosixSpawn(const char *exe_path,
}
}
+ if (launch_info.GetFlags().Test(eLaunchFlagMemoryTagging)) {
+ // The following function configures the spawn attributes to launch the
+ // process with memory tagging explicitly enabled. We look it up
+ // dynamically since it is only available on newer OS. Does nothing on
+ // hardware which does not support MTE.
+ //
+ // int posix_spawnattr_set_use_sec_transition_shims_np(
+ // posix_spawnattr_t *attr, uint32_t flags);
+ //
+ using posix_spawnattr_set_use_sec_transition_shims_np_t =
+ int (*)(posix_spawnattr_t *attr, uint32_t flags);
+ auto posix_spawnattr_enable_memory_tagging_fn =
+ (posix_spawnattr_set_use_sec_transition_shims_np_t)dlsym(
+ RTLD_DEFAULT, "posix_spawnattr_set_use_sec_transition_shims_np");
+ if (posix_spawnattr_enable_memory_tagging_fn) {
+ error = Status(posix_spawnattr_enable_memory_tagging_fn(&attr, 0),
+ eErrorTypePOSIX);
+ if (error.Fail()) {
+ LLDB_LOG(log,
+ "error: {0}, "
+ "posix_spawnattr_set_use_sec_transition_shims_np(&attr, 0)",
+ error);
+ return error;
+ }
+ } else {
+ LLDB_LOG(log,
+ "error: posix_spawnattr_set_use_sec_transition_shims_np not "
+ "available",
+ error);
+ }
+ }
+
// Don't set the binpref if a shell was provided. After all, that's only
// going to affect what version of the shell is launched, not what fork of
// the binary is launched. We insert "arch --arch <ARCH> as part of the
diff --git a/lldb/source/Host/windows/MainLoopWindows.cpp b/lldb/source/Host/windows/MainLoopWindows.cpp
index c0b10797e506..9b7df10258bc 100644
--- a/lldb/source/Host/windows/MainLoopWindows.cpp
+++ b/lldb/source/Host/windows/MainLoopWindows.cpp
@@ -55,11 +55,7 @@ public:
if (m_monitor_thread.joinable()) {
m_stopped = true;
SetEvent(m_ready);
- // Keep trying to cancel ReadFile() until the thread exits.
- do {
- CancelIoEx(m_handle, /*lpOverlapped=*/NULL);
- } while (WaitForSingleObject(m_monitor_thread.native_handle(), 1) ==
- WAIT_TIMEOUT);
+ CancelIoEx(m_handle, /*lpOverlapped=*/NULL);
m_monitor_thread.join();
}
CloseHandle(m_event);
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index d909c5650c95..ffcc9ceeb2a9 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2591,7 +2591,7 @@ void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) {
llvm::sys::path::parent_path(home_init_file.GetPath())) {
result.SetStatus(eReturnStatusSuccessFinishNoResult);
} else {
- result.AppendError(InitFileWarning);
+ result.AppendWarning(InitFileWarning);
}
}
}
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 6a654a0dafe5..ca768db1199c 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -81,6 +81,12 @@ lldb::BreakpointSP ScriptInterpreter::GetOpaqueTypeFromSBBreakpoint(
return breakpoint.m_opaque_wp.lock();
}
+lldb::BreakpointLocationSP
+ScriptInterpreter::GetOpaqueTypeFromSBBreakpointLocation(
+ const lldb::SBBreakpointLocation &break_loc) const {
+ return break_loc.m_opaque_wp.lock();
+}
+
lldb::ProcessAttachInfoSP ScriptInterpreter::GetOpaqueTypeFromSBAttachInfo(
const lldb::SBAttachInfo &attach_info) const {
return attach_info.m_opaque_sp;
@@ -100,6 +106,13 @@ ScriptInterpreter::GetStatusFromSBError(const lldb::SBError &error) const {
return Status();
}
+lldb::StackFrameSP
+ScriptInterpreter::GetOpaqueTypeFromSBFrame(const lldb::SBFrame &frame) const {
+ if (frame.m_opaque_sp)
+ return frame.m_opaque_sp->GetFrameSP();
+ return nullptr;
+}
+
Event *
ScriptInterpreter::GetOpaqueTypeFromSBEvent(const lldb::SBEvent &event) const {
return event.m_opaque_ptr;
diff --git a/lldb/source/Interpreter/embedded_interpreter.py b/lldb/source/Interpreter/embedded_interpreter.py
index a487592ef1ae..cdf166a4d1a2 100644
--- a/lldb/source/Interpreter/embedded_interpreter.py
+++ b/lldb/source/Interpreter/embedded_interpreter.py
@@ -1,9 +1,5 @@
import sys
-
-if sys.version_info[0] < 3:
- import __builtin__ as builtins
-else:
- import builtins
+import builtins
import code
import lldb
import traceback
diff --git a/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp b/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp
index e6c8c8b46987..7bf99ce7bdde 100644
--- a/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp
+++ b/lldb/source/Plugins/ABI/LoongArch/ABISysV_loongarch.cpp
@@ -597,15 +597,16 @@ bool ABISysV_loongarch::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
return llvm::StringSwitch<bool>(name)
// integer ABI names
- .Cases("ra", "sp", "fp", true)
- .Cases("s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", true)
+ .Cases({"ra", "sp", "fp"}, true)
+ .Cases({"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9"}, true)
// integer hardware names
- .Cases("r1", "r3", "r22", true)
- .Cases("r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "31", true)
+ .Cases({"r1", "r3", "r22"}, true)
+ .Cases({"r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "31"},
+ true)
// floating point ABI names
- .Cases("fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", is_hw_fp)
+ .Cases({"fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7"}, is_hw_fp)
// floating point hardware names
- .Cases("f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", is_hw_fp)
+ .Cases({"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31"}, is_hw_fp)
.Default(false);
}
diff --git a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
index b313ca03fb97..822c93dbbec3 100644
--- a/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
+++ b/lldb/source/Plugins/ABI/RISCV/ABISysV_riscv.cpp
@@ -783,21 +783,22 @@ bool ABISysV_riscv::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
bool is_callee_saved =
llvm::StringSwitch<bool>(name)
// integer ABI names
- .Cases("ra", "sp", "fp", true)
- .Cases("s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9",
+ .Cases({"ra", "sp", "fp"}, true)
+ .Cases({"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9"},
true)
- .Cases("s10", "s11", true)
+ .Cases({"s10", "s11"}, true)
// integer hardware names
- .Cases("x1", "x2", "x8", "x9", "x18", "x19", "x20", "x21", "x22",
+ .Cases({"x1", "x2", "x8", "x9", "x18", "x19", "x20", "x21", "x22"},
true)
- .Cases("x23", "x24", "x25", "x26", "x27", true)
+ .Cases({"x23", "x24", "x25", "x26", "x27"}, true)
// floating point ABI names
- .Cases("fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7",
+ .Cases({"fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7"},
is_hw_fp)
- .Cases("fs8", "fs9", "fs10", "fs11", is_hw_fp)
+ .Cases({"fs8", "fs9", "fs10", "fs11"}, is_hw_fp)
// floating point hardware names
- .Cases("f8", "f9", "f18", "f19", "f20", "f21", "f22", "f23", is_hw_fp)
- .Cases("f24", "f25", "f26", "f27", is_hw_fp)
+ .Cases({"f8", "f9", "f18", "f19", "f20", "f21", "f22", "f23"},
+ is_hw_fp)
+ .Cases({"f24", "f25", "f26", "f27"}, is_hw_fp)
.Default(false);
return is_callee_saved;
diff --git a/lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp b/lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp
index 7646ccda010d..effb3de8215d 100644
--- a/lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp
+++ b/lldb/source/Plugins/ABI/X86/ABISysV_x86_64.cpp
@@ -929,8 +929,8 @@ bool ABISysV_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
std::string Name = std::string(reg_info->name);
bool IsCalleeSaved =
llvm::StringSwitch<bool>(Name)
- .Cases("r12", "r13", "r14", "r15", "rbp", "ebp", "rbx", "ebx", true)
- .Cases("rip", "eip", "rsp", "esp", "sp", "fp", "pc", true)
+ .Cases({"r12", "r13", "r14", "r15", "rbp", "ebp", "rbx", "ebx"}, true)
+ .Cases({"rip", "eip", "rsp", "esp", "sp", "fp", "pc"}, true)
.Default(false);
return IsCalleeSaved;
}
diff --git a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
index 56df6f6b5e97..339012cffb68 100644
--- a/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
+++ b/lldb/source/Plugins/ABI/X86/ABIWindows_x86_64.cpp
@@ -797,10 +797,11 @@ bool ABIWindows_x86_64::RegisterIsCalleeSaved(const RegisterInfo *reg_info) {
std::string Name = std::string(reg_info->name);
bool IsCalleeSaved =
llvm::StringSwitch<bool>(Name)
- .Cases("rbx", "ebx", "rbp", "ebp", "rdi", "edi", "rsi", "esi", true)
- .Cases("rsp", "esp", "r12", "r13", "r14", "r15", "sp", "fp", true)
- .Cases("xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12",
- "xmm13", "xmm14", "xmm15", true)
+ .Cases({"rbx", "ebx", "rbp", "ebp", "rdi", "edi", "rsi", "esi"}, true)
+ .Cases({"rsp", "esp", "r12", "r13", "r14", "r15", "sp", "fp"}, true)
+ .Cases({"xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12",
+ "xmm13", "xmm14", "xmm15"},
+ true)
.Default(false);
return IsCalleeSaved;
}
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
index 92094c01e2a5..e3b6ff8f17b4 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp
@@ -332,8 +332,7 @@ CompilerType ClangASTImporter::DeportType(TypeSystemClang &dst,
DeclContextOverride decl_context_override;
if (auto *t = ClangUtil::GetQualType(src_type)->getAs<TagType>())
- decl_context_override.OverrideAllDeclsFromContainingFunction(
- t->getOriginalDecl());
+ decl_context_override.OverrideAllDeclsFromContainingFunction(t->getDecl());
CompleteTagDeclsScope complete_scope(*this, &dst.getASTContext(),
&src_ctxt->getASTContext());
@@ -393,7 +392,7 @@ bool ClangASTImporter::CanImport(const CompilerType &type) {
case clang::Type::Record:
return CanImport(qual_type->getAsCXXRecordDecl());
case clang::Type::Enum:
- return CanImport(llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl());
+ return CanImport(llvm::cast<clang::EnumType>(qual_type)->getDecl());
case clang::Type::ObjCObject:
case clang::Type::ObjCInterface: {
const clang::ObjCObjectType *objc_class_type =
@@ -452,7 +451,7 @@ bool ClangASTImporter::Import(const CompilerType &type) {
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
if (enum_decl) {
if (GetDeclOrigin(enum_decl).Valid())
return CompleteAndFetchChildren(qual_type);
@@ -591,7 +590,7 @@ bool ExtractBaseOffsets(const ASTRecordLayout &record_layout,
return false;
DeclFromUser<RecordDecl> origin_base_record(
- origin_base_record_type->getOriginalDecl());
+ origin_base_record_type->getDecl());
if (origin_base_record.IsInvalid())
return false;
@@ -722,8 +721,7 @@ bool ClangASTImporter::importRecordLayoutFromOrigin(
QualType base_type = bi->getType();
const RecordType *base_record_type = base_type->getAs<RecordType>();
- DeclFromParser<RecordDecl> base_record(
- base_record_type->getOriginalDecl());
+ DeclFromParser<RecordDecl> base_record(base_record_type->getDecl());
DeclFromParser<CXXRecordDecl> base_cxx_record =
DynCast<CXXRecordDecl>(base_record);
@@ -855,7 +853,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->getOriginalDecl();
+ TagDecl *tag_decl = tag_type->getDecl();
DeclOrigin decl_origin = GetDeclOrigin(tag_decl);
@@ -923,7 +921,7 @@ bool ClangASTImporter::RequireCompleteType(clang::QualType type) {
return false;
if (const TagType *tag_type = type->getAs<TagType>()) {
- TagDecl *tag_decl = tag_type->getOriginalDecl();
+ TagDecl *tag_decl = tag_type->getDecl();
if (tag_decl->getDefinition())
return true;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp
index 21a930745893..ebe7be43e5dd 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 =
- tag_type->getOriginalDecl()->getDefinitionOrSelf();
+ tag_type->getDecl()->getDefinitionOrSelf();
if (TypeSystemClang::GetCompleteDecl(
&candidate_tag_decl->getASTContext(), candidate_tag_decl))
@@ -250,8 +250,7 @@ TagDecl *ClangASTSource::FindCompleteType(const TagDecl *decl) {
if (!tag_type)
continue;
- TagDecl *candidate_tag_decl =
- tag_type->getOriginalDecl()->getDefinitionOrSelf();
+ TagDecl *candidate_tag_decl = tag_type->getDecl()->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 8a68282bbbf3..833bc3b7fc25 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -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->getOriginalDecl()->getDefinitionOrSelf());
+ CompleteType(tag_type->getDecl()->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/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
index 924953cc43fa..6b121c934dfb 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp
@@ -74,6 +74,7 @@
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Disassembler.h"
#include "lldb/Core/Module.h"
+#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Host/File.h"
@@ -96,6 +97,7 @@
#include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h"
#include "Plugins/Platform/MacOSX/PlatformDarwin.h"
#include "lldb/Utility/XcodeSDK.h"
+#include "lldb/lldb-enumerations.h"
#include <cctype>
#include <memory>
@@ -527,7 +529,8 @@ static void SetupTargetOpts(CompilerInstance &compiler,
static void SetupLangOpts(CompilerInstance &compiler,
ExecutionContextScope &exe_scope,
- const Expression &expr) {
+ const Expression &expr,
+ DiagnosticManager &diagnostic_manager) {
Log *log = GetLog(LLDBLog::Expressions);
// If the expression is being evaluated in the context of an existing stack
@@ -547,6 +550,9 @@ static void SetupLangOpts(CompilerInstance &compiler,
: lldb::eLanguageTypeUnknown),
lldb_private::Language::GetNameForLanguageType(language));
+ lldb::LanguageType language_for_note = language;
+ std::string language_fallback_reason;
+
LangOptions &lang_opts = compiler.getLangOpts();
switch (language) {
@@ -560,6 +566,10 @@ static void SetupLangOpts(CompilerInstance &compiler,
// family language, because the expression parser uses features of C++ to
// capture values.
lang_opts.CPlusPlus = true;
+
+ language_for_note = lldb::eLanguageTypeC_plus_plus;
+ language_fallback_reason =
+ "Expression evaluation in pure C not supported. ";
break;
case lldb::eLanguageTypeObjC:
lang_opts.ObjC = true;
@@ -567,6 +577,10 @@ static void SetupLangOpts(CompilerInstance &compiler,
// to "ask for ObjC, get ObjC++" (see comment above).
lang_opts.CPlusPlus = true;
+ language_for_note = lldb::eLanguageTypeObjC_plus_plus;
+ language_fallback_reason =
+ "Expression evaluation in pure Objective-C not supported. ";
+
// Clang now sets as default C++14 as the default standard (with
// GNU extensions), so we do the same here to avoid mismatches that
// cause compiler error when evaluating expressions (e.g. nullptr not found
@@ -607,9 +621,27 @@ static void SetupLangOpts(CompilerInstance &compiler,
lang_opts.CPlusPlus = true;
lang_opts.CPlusPlus11 = true;
compiler.getHeaderSearchOpts().UseLibcxx = true;
+
+ language_for_note = lldb::eLanguageTypeObjC_plus_plus;
+ if (language != language_for_note) {
+ if (language != lldb::eLanguageTypeUnknown)
+ language_fallback_reason = llvm::formatv(
+ "Expression evaluation in {0} not supported. ",
+ lldb_private::Language::GetDisplayNameForLanguageType(language));
+
+ language_fallback_reason +=
+ llvm::formatv("Falling back to default language. ");
+ }
break;
}
+ diagnostic_manager.AddDiagnostic(
+ llvm::formatv("{0}Ran expression as '{1}'.", language_fallback_reason,
+ lldb_private::Language::GetDisplayNameForLanguageType(
+ language_for_note))
+ .str(),
+ lldb::Severity::eSeverityInfo, DiagnosticOrigin::eDiagnosticOriginLLDB);
+
lang_opts.Bool = true;
lang_opts.WChar = true;
lang_opts.Blocks = true;
@@ -687,8 +719,8 @@ static void SetupImportStdModuleLangOpts(CompilerInstance &compiler,
ClangExpressionParser::ClangExpressionParser(
ExecutionContextScope *exe_scope, Expression &expr,
- bool generate_debug_info, std::vector<std::string> include_directories,
- std::string filename)
+ bool generate_debug_info, DiagnosticManager &diagnostic_manager,
+ std::vector<std::string> include_directories, std::string filename)
: ExpressionParser(exe_scope, expr, generate_debug_info), m_compiler(),
m_pp_callbacks(nullptr),
m_include_directories(std::move(include_directories)),
@@ -754,7 +786,7 @@ ClangExpressionParser::ClangExpressionParser(
}
// 4. Set language options.
- SetupLangOpts(*m_compiler, *exe_scope, expr);
+ SetupLangOpts(*m_compiler, *exe_scope, expr, diagnostic_manager);
auto *clang_expr = dyn_cast<ClangUserExpression>(&m_expr);
if (clang_expr && clang_expr->DidImportCxxModules()) {
LLDB_LOG(log, "Adding lang options for importing C++ modules");
@@ -792,7 +824,7 @@ ClangExpressionParser::ClangExpressionParser(
// 6. Set up the source management objects inside the compiler
m_compiler->createFileManager();
if (!m_compiler->hasSourceManager())
- m_compiler->createSourceManager(m_compiler->getFileManager());
+ m_compiler->createSourceManager();
m_compiler->createPreprocessor(TU_Complete);
switch (expr.Language().AsLanguageType()) {
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
index 93e0b007dbcc..734ad51c9646 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.h
@@ -65,6 +65,7 @@ public:
/// diagnostics (i.e. errors, warnings or notes from Clang).
ClangExpressionParser(ExecutionContextScope *exe_scope, Expression &expr,
bool generate_debug_info,
+ DiagnosticManager &diagnostic_manager,
std::vector<std::string> include_directories = {},
std::string filename = "<clang expression>");
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
index e4a094f3aa51..d2db319afb7a 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangFunctionCaller.cpp
@@ -189,8 +189,8 @@ ClangFunctionCaller::CompileFunction(lldb::ThreadSP thread_to_use_sp,
lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock());
if (jit_process_sp) {
const bool generate_debug_info = true;
- auto *clang_parser = new ClangExpressionParser(jit_process_sp.get(), *this,
- generate_debug_info);
+ auto *clang_parser = new ClangExpressionParser(
+ jit_process_sp.get(), *this, generate_debug_info, diagnostic_manager);
num_errors = clang_parser->Parse(diagnostic_manager);
m_parser.reset(clang_parser);
} else {
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
index 6b743e29e21f..e8d5ec3c7fd9 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -574,7 +574,7 @@ bool ClangUserExpression::TryParse(
m_parser = std::make_unique<ClangExpressionParser>(
exe_ctx.GetBestExecutionContextScope(), *this, generate_debug_info,
- m_include_directories, m_filename);
+ diagnostic_manager, m_include_directories, m_filename);
unsigned num_errors = m_parser->Parse(diagnostic_manager);
@@ -818,7 +818,7 @@ bool ClangUserExpression::Complete(ExecutionContext &exe_ctx,
}
ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
- false);
+ false, diagnostic_manager);
// We have to find the source code location where the user text is inside
// the transformed expression code. When creating the transformed text, we
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
index 1f44200c4cff..e6983066a12f 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp
@@ -120,7 +120,7 @@ bool ClangUtilityFunction::Install(DiagnosticManager &diagnostic_manager,
const bool generate_debug_info = true;
ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this,
- generate_debug_info);
+ generate_debug_info, diagnostic_manager);
unsigned num_errors = parser.Parse(diagnostic_manager);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp
index 6f57c1806367..794b194ec47d 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->getOriginalDecl()->getDefinitionOrSelf();
+ TagDecl *tag_decl = tag_type->getDecl()->getDefinitionOrSelf();
m_decls.push_back(tag_decl);
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 4e8a430af8c6..a2199cb65cd3 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -104,10 +104,10 @@ CPlusPlusLanguage::GetFunctionNameInfo(ConstString name) const {
}
bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const {
- const char *mangled_name = mangled.GetMangledName().GetCString();
- auto mangling_scheme = Mangled::GetManglingScheme(mangled_name);
- return mangled_name && (mangling_scheme == Mangled::eManglingSchemeItanium ||
- mangling_scheme == Mangled::eManglingSchemeMSVC);
+ auto mangling_scheme =
+ Mangled::GetManglingScheme(mangled.GetMangledName().GetStringRef());
+ return mangling_scheme == Mangled::eManglingSchemeItanium ||
+ mangling_scheme == Mangled::eManglingSchemeMSVC;
}
ConstString CPlusPlusLanguage::GetDemangledFunctionNameWithoutArguments(
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
index 606f9517ac0c..e20dd31ef70a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.cpp
@@ -142,7 +142,13 @@ lldb_private::formatters::LibcxxStdAtomicSyntheticFrontEnd::
SyntheticChildrenFrontEnd *
lldb_private::formatters::LibcxxAtomicSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
- if (valobj_sp)
+ if (valobj_sp && IsLibCxxAtomic(*valobj_sp))
return new LibcxxStdAtomicSyntheticFrontEnd(valobj_sp);
return nullptr;
}
+
+bool lldb_private::formatters::IsLibCxxAtomic(ValueObject &valobj) {
+ if (auto valobj_sp = valobj.GetNonSyntheticValue())
+ return valobj_sp->GetChildMemberWithName("__a_") != nullptr;
+ return false;
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
index 93274460ce35..7005950017a5 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxAtomic.h
@@ -18,6 +18,8 @@
namespace lldb_private {
namespace formatters {
+bool IsLibCxxAtomic(ValueObject &valobj);
+
lldb::ValueObjectSP GetLibCxxAtomicValue(ValueObject &valobj);
bool LibCxxAtomicSummaryProvider(ValueObject &valobj, Stream &stream,
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 4b183a8d62e5..5588208a3ef8 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -52,7 +52,7 @@ private:
ValueObject *m_tree = nullptr;
size_t m_num_elements = 0;
ValueObject *m_next_element = nullptr;
- std::vector<std::pair<ValueObject *, uint64_t>> m_elements_cache;
+ std::vector<ValueObject *> m_elements_cache;
};
class LibCxxUnorderedMapIteratorSyntheticFrontEnd
@@ -192,26 +192,25 @@ lldb::ValueObjectSP lldb_private::formatters::
return nullptr;
}
}
- m_elements_cache.push_back(
- {value_sp.get(), hash_sp->GetValueAsUnsigned(0)});
+ m_elements_cache.push_back(value_sp.get());
m_next_element = node_sp->GetChildMemberWithName("__next_").get();
if (!m_next_element || m_next_element->GetValueAsUnsigned(0) == 0)
m_next_element = nullptr;
}
- std::pair<ValueObject *, uint64_t> val_hash = m_elements_cache[idx];
- if (!val_hash.first)
+ ValueObject *val_hash = m_elements_cache[idx];
+ if (!val_hash)
return lldb::ValueObjectSP();
StreamString stream;
stream.Printf("[%" PRIu64 "]", (uint64_t)idx);
DataExtractor data;
Status error;
- val_hash.first->GetData(data, error);
+ val_hash->GetData(data, error);
if (error.Fail())
return lldb::ValueObjectSP();
const bool thread_and_frame_only_if_stopped = true;
- ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
- thread_and_frame_only_if_stopped);
+ ExecutionContext exe_ctx =
+ val_hash->GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped);
return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
m_element_type);
}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
index 8a4918127584..e818b88e202e 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStl.h
@@ -89,6 +89,7 @@ MsvcStlVariantSyntheticFrontEndCreator(CXXSyntheticChildren *,
lldb::ValueObjectSP valobj_sp);
// MSVC STL std::atomic<>
+bool IsMsvcStlAtomic(ValueObject &valobj);
bool MsvcStlAtomicSummaryProvider(ValueObject &valobj, Stream &stream,
const TypeSummaryOptions &options);
SyntheticChildrenFrontEnd *
diff --git a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
index 3ec324577ac7..020ba1016623 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/MsvcStlAtomic.cpp
@@ -50,7 +50,7 @@ llvm::Expected<uint32_t> lldb_private::formatters::
lldb::ValueObjectSP
lldb_private::formatters::MsvcStlAtomicSyntheticFrontEnd::GetChildAtIndex(
uint32_t idx) {
- if (idx == 0)
+ if (idx == 0 && m_storage && m_element_type.IsValid())
return m_storage->Cast(m_element_type)->Clone(ConstString("Value"));
return nullptr;
}
@@ -83,7 +83,9 @@ llvm::Expected<size_t> lldb_private::formatters::
lldb_private::SyntheticChildrenFrontEnd *
lldb_private::formatters::MsvcStlAtomicSyntheticFrontEndCreator(
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
- return new MsvcStlAtomicSyntheticFrontEnd(valobj_sp);
+ if (valobj_sp && IsMsvcStlAtomic(*valobj_sp))
+ return new MsvcStlAtomicSyntheticFrontEnd(valobj_sp);
+ return nullptr;
}
bool lldb_private::formatters::MsvcStlAtomicSummaryProvider(
@@ -100,3 +102,9 @@ bool lldb_private::formatters::MsvcStlAtomicSummaryProvider(
}
return false;
}
+
+bool lldb_private::formatters::IsMsvcStlAtomic(ValueObject &valobj) {
+ if (auto valobj_sp = valobj.GetNonSyntheticValue())
+ return valobj_sp->GetChildMemberWithName("_Storage") != nullptr;
+ return false;
+}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
index cc0c9e728964..460c503b25c7 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.cpp
@@ -14,6 +14,7 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/Sequence.h"
using namespace lldb;
using namespace lldb_private;
@@ -259,6 +260,7 @@ bool ClassDescriptorV2::method_list_t::Read(Process *process,
uint32_t entsize = extractor.GetU32_unchecked(&cursor);
m_is_small = (entsize & 0x80000000) != 0;
m_has_direct_selector = (entsize & 0x40000000) != 0;
+ m_has_relative_types = (entsize & 0x20000000) != 0;
m_entsize = entsize & 0xfffc;
m_count = extractor.GetU32_unchecked(&cursor);
m_first_ptr = addr + cursor;
@@ -266,22 +268,49 @@ bool ClassDescriptorV2::method_list_t::Read(Process *process,
return true;
}
-bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
- lldb::addr_t relative_selector_base_addr,
- bool is_small, bool has_direct_sel) {
- size_t ptr_size = process->GetAddressByteSize();
- size_t size = GetSize(process, is_small);
+llvm::SmallVector<ClassDescriptorV2::method_t, 0>
+ClassDescriptorV2::ReadMethods(llvm::ArrayRef<lldb::addr_t> addresses,
+ lldb::addr_t relative_string_base_addr,
+ bool is_small, bool has_direct_sel,
+ bool has_relative_types) const {
+ lldb_private::Process *process = m_runtime.GetProcess();
+ if (!process)
+ return {};
- DataBufferHeap buffer(size, '\0');
- Status error;
+ const size_t size = method_t::GetSize(process, is_small);
+ const size_t num_methods = addresses.size();
- process->ReadMemory(addr, buffer.GetBytes(), size, error);
- if (error.Fail()) {
- return false;
+ llvm::SmallVector<uint8_t, 0> buffer(num_methods * size, 0);
+ llvm::DenseSet<uint32_t> failed_indices;
+
+ for (auto [idx, addr] : llvm::enumerate(addresses)) {
+ Status error;
+ process->ReadMemory(addr, buffer.data() + idx * size, size, error);
+ if (error.Fail())
+ failed_indices.insert(idx);
}
- DataExtractor extractor(buffer.GetBytes(), size, process->GetByteOrder(),
- ptr_size);
+ llvm::SmallVector<method_t, 0> methods;
+ methods.reserve(num_methods);
+ for (auto [idx, addr] : llvm::enumerate(addresses)) {
+ if (failed_indices.contains(idx))
+ continue;
+ DataExtractor extractor(buffer.data() + idx * size, size,
+ process->GetByteOrder(),
+ process->GetAddressByteSize());
+ methods.push_back(method_t());
+ methods.back().Read(extractor, process, addr, relative_string_base_addr,
+ is_small, has_direct_sel, has_relative_types);
+ }
+
+ return methods;
+}
+
+bool ClassDescriptorV2::method_t::Read(DataExtractor &extractor,
+ Process *process, lldb::addr_t addr,
+ lldb::addr_t relative_string_base_addr,
+ bool is_small, bool has_direct_sel,
+ bool has_relative_types) {
lldb::offset_t cursor = 0;
if (is_small) {
@@ -291,16 +320,19 @@ bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
m_name_ptr = addr + nameref_offset;
+ Status error;
if (!has_direct_sel) {
// The SEL offset points to a SELRef. We need to dereference twice.
- m_name_ptr = process->ReadUnsignedIntegerFromMemory(m_name_ptr, ptr_size,
- 0, error);
- if (!error.Success())
+ m_name_ptr = process->ReadPointerFromMemory(m_name_ptr, error);
+ if (error.Fail())
return false;
- } else if (relative_selector_base_addr != LLDB_INVALID_ADDRESS) {
- m_name_ptr = relative_selector_base_addr + nameref_offset;
+ } else if (relative_string_base_addr != LLDB_INVALID_ADDRESS) {
+ m_name_ptr = relative_string_base_addr + nameref_offset;
}
- m_types_ptr = addr + 4 + types_offset;
+ if (has_relative_types)
+ m_types_ptr = relative_string_base_addr + types_offset;
+ else
+ m_types_ptr = addr + 4 + types_offset;
m_imp_ptr = addr + 8 + imp_offset;
} else {
m_name_ptr = extractor.GetAddress_unchecked(&cursor);
@@ -308,13 +340,13 @@ bool ClassDescriptorV2::method_t::Read(Process *process, lldb::addr_t addr,
m_imp_ptr = extractor.GetAddress_unchecked(&cursor);
}
+ Status error;
process->ReadCStringFromMemory(m_name_ptr, m_name, error);
- if (error.Fail()) {
+ if (error.Fail())
return false;
- }
process->ReadCStringFromMemory(m_types_ptr, m_types, error);
- return !error.Fail();
+ return error.Success();
}
bool ClassDescriptorV2::ivar_list_t::Read(Process *process, lldb::addr_t addr) {
@@ -447,17 +479,20 @@ ClassDescriptorV2::GetMethodList(Process *process,
bool ClassDescriptorV2::ProcessMethodList(
std::function<bool(const char *, const char *)> const &instance_method_func,
ClassDescriptorV2::method_list_t &method_list) const {
- lldb_private::Process *process = m_runtime.GetProcess();
- auto method = std::make_unique<method_t>();
- lldb::addr_t relative_selector_base_addr =
- m_runtime.GetRelativeSelectorBaseAddr();
- for (uint32_t i = 0, e = method_list.m_count; i < e; ++i) {
- method->Read(process, method_list.m_first_ptr + (i * method_list.m_entsize),
- relative_selector_base_addr, method_list.m_is_small,
- method_list.m_has_direct_selector);
- if (instance_method_func(method->m_name.c_str(), method->m_types.c_str()))
+ auto idx_to_method_addr = [&](uint32_t idx) {
+ return method_list.m_first_ptr + (idx * method_list.m_entsize);
+ };
+ llvm::SmallVector<addr_t> addresses = llvm::to_vector(llvm::map_range(
+ llvm::seq<uint32_t>(method_list.m_count), idx_to_method_addr));
+
+ llvm::SmallVector<method_t, 0> methods =
+ ReadMethods(addresses, m_runtime.GetRelativeSelectorBaseAddr(),
+ method_list.m_is_small, method_list.m_has_direct_selector,
+ method_list.m_has_relative_types);
+
+ for (const auto &method : methods)
+ if (instance_method_func(method.m_name.c_str(), method.m_types.c_str()))
break;
- }
return true;
}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
index 920a5eba20ab..0fff9af43836 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCClassDescriptorV2.h
@@ -143,6 +143,7 @@ private:
uint16_t m_entsize;
bool m_is_small;
bool m_has_direct_selector;
+ bool m_has_relative_types;
uint32_t m_count;
lldb::addr_t m_first_ptr;
@@ -172,11 +173,16 @@ private:
+ field_size; // IMP imp;
}
- bool Read(Process *process, lldb::addr_t addr,
- lldb::addr_t relative_selector_base_addr, bool is_small,
- bool has_direct_sel);
+ bool Read(DataExtractor &extractor, Process *process, lldb::addr_t addr,
+ lldb::addr_t relative_string_base_addr, bool is_small,
+ bool has_direct_sel, bool has_relative_types);
};
+ llvm::SmallVector<method_t, 0>
+ ReadMethods(llvm::ArrayRef<lldb::addr_t> addresses,
+ lldb::addr_t relative_string_base_addr, bool is_small,
+ bool has_direct_sel, bool has_relative_types) const;
+
struct ivar_list_t {
uint32_t m_entsize;
uint32_t m_count;
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
index c33760eccd12..2b2ca080c7f5 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.cpp
@@ -423,6 +423,46 @@ Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(
return error;
}
+CompilerType ObjCLanguageRuntime::LookupInModulesVendor(ConstString class_name,
+ Target &target) {
+ assert(class_name);
+
+ auto *persistent_state = llvm::cast<ClangPersistentVariables>(
+ target.GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC));
+ if (!persistent_state)
+ return {};
+
+ auto clang_modules_decl_vendor_sp =
+ persistent_state->GetClangModulesDeclVendor();
+ if (!clang_modules_decl_vendor_sp)
+ return {};
+
+ auto types = clang_modules_decl_vendor_sp->FindTypes(
+ class_name, /*max_matches*/ UINT32_MAX);
+ if (types.empty())
+ return {};
+
+ return types.front();
+}
+
+CompilerType ObjCLanguageRuntime::LookupInRuntime(ConstString class_name) {
+ auto *runtime_vendor = GetDeclVendor();
+ if (!runtime_vendor)
+ return {};
+
+ std::vector<CompilerDecl> compiler_decls;
+ runtime_vendor->FindDecls(class_name, false, UINT32_MAX, compiler_decls);
+ if (compiler_decls.empty())
+ return {};
+
+ auto *ctx =
+ llvm::dyn_cast<TypeSystemClang>(compiler_decls[0].GetTypeSystem());
+ if (!ctx)
+ return {};
+
+ return ctx->GetTypeForDecl(compiler_decls[0].GetOpaqueDecl());
+}
+
std::optional<CompilerType>
ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {
CompilerType class_type;
@@ -442,18 +482,21 @@ ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {
if (!class_name)
return std::nullopt;
- TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name);
- if (!complete_objc_class_type_sp)
- return std::nullopt;
-
- CompilerType complete_class(
- complete_objc_class_type_sp->GetFullCompilerType());
- if (complete_class.GetCompleteType()) {
- if (is_pointer_type)
- return complete_class.GetPointerType();
- else
- return complete_class;
+ if (TypeSP complete_objc_class_type_sp =
+ LookupInCompleteClassCache(class_name)) {
+ if (CompilerType complete_class =
+ complete_objc_class_type_sp->GetFullCompilerType();
+ complete_class.GetCompleteType())
+ return is_pointer_type ? complete_class.GetPointerType() : complete_class;
}
+ assert(m_process);
+ if (CompilerType found =
+ LookupInModulesVendor(class_name, m_process->GetTarget()))
+ return is_pointer_type ? found.GetPointerType() : found;
+
+ if (CompilerType found = LookupInRuntime(class_name))
+ return is_pointer_type ? found.GetPointerType() : found;
+
return std::nullopt;
}
diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
index 45de098c15f5..cc8281e9d1a0 100644
--- a/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
+++ b/lldb/source/Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h
@@ -465,6 +465,10 @@ protected:
ObjCLanguageRuntime(const ObjCLanguageRuntime &) = delete;
const ObjCLanguageRuntime &operator=(const ObjCLanguageRuntime &) = delete;
+
+private:
+ CompilerType LookupInRuntime(ConstString class_name);
+ CompilerType LookupInModulesVendor(ConstString class_name, Target &process);
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
index fada1fda2b4b..9cdb8467bfc6 100644
--- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
+++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp
@@ -2067,6 +2067,43 @@ static bool ParseTrieEntries(DataExtractor &data, lldb::offset_t offset,
return true;
}
+static bool
+TryParseV2ObjCMetadataSymbol(const char *&symbol_name,
+ const char *&symbol_name_non_abi_mangled,
+ SymbolType &type) {
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_class("_OBJC_CLASS_$_");
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_metaclass(
+ "_OBJC_METACLASS_$_");
+ static constexpr llvm::StringLiteral g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
+
+ llvm::StringRef symbol_name_ref(symbol_name);
+ if (symbol_name_ref.empty())
+ return false;
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_class.size();
+ type = eSymbolTypeObjCClass;
+ return true;
+ }
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_metaclass)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
+ type = eSymbolTypeObjCMetaClass;
+ return true;
+ }
+
+ if (symbol_name_ref.starts_with(g_objc_v2_prefix_ivar)) {
+ symbol_name_non_abi_mangled = symbol_name + 1;
+ symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
+ type = eSymbolTypeObjCIVar;
+ return true;
+ }
+
+ return false;
+}
+
static SymbolType GetSymbolType(const char *&symbol_name,
bool &demangled_is_synthesized,
const SectionSP &text_section_sp,
@@ -2183,9 +2220,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
uint32_t i;
FileSpecList dylib_files;
- llvm::StringRef g_objc_v2_prefix_class("_OBJC_CLASS_$_");
- llvm::StringRef g_objc_v2_prefix_metaclass("_OBJC_METACLASS_$_");
- llvm::StringRef g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
UUID image_uuid;
for (i = 0; i < m_header.ncmds; ++i) {
@@ -2805,36 +2839,15 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
is_gsym = true;
sym[sym_idx].SetExternal(true);
- if (symbol_name && symbol_name[0] == '_' &&
- symbol_name[1] == 'O') {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
-
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name, symbol_name_non_abi_mangled,
+ type)) {
+ demangled_is_synthesized = true;
} else {
if (nlist.n_value != 0)
symbol_section = section_info.GetSection(
nlist.n_sect, nlist.n_value);
+
type = eSymbolTypeData;
}
break;
@@ -3320,48 +3333,10 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
symbol_sect_name) {
type = eSymbolTypeRuntime;
- if (symbol_name) {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with("_OBJC_")) {
- llvm::StringRef
- g_objc_v2_prefix_class(
- "_OBJC_CLASS_$_");
- llvm::StringRef
- g_objc_v2_prefix_metaclass(
- "_OBJC_METACLASS_$_");
- llvm::StringRef
- g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
- if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
- } else if (
- symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled =
- symbol_name + 1;
- symbol_name =
- symbol_name +
- g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
- }
- }
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name,
+ symbol_name_non_abi_mangled, type))
+ demangled_is_synthesized = true;
} else if (symbol_sect_name &&
::strstr(symbol_sect_name,
"__gcc_except_tab") ==
@@ -3652,7 +3627,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
if (is_debug) {
switch (nlist.n_type) {
- case N_GSYM:
+ case N_GSYM: {
// global symbol: name,,NO_SECT,type,0
// Sometimes the N_GSYM value contains the address.
@@ -3668,33 +3643,17 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
is_gsym = true;
sym[sym_idx].SetExternal(true);
- if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
-
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
+ if (TryParseV2ObjCMetadataSymbol(symbol_name,
+ symbol_name_non_abi_mangled, type)) {
+ demangled_is_synthesized = true;
} else {
if (nlist.n_value != 0)
symbol_section =
section_info.GetSection(nlist.n_sect, nlist.n_value);
+
type = eSymbolTypeData;
}
- break;
+ } break;
case N_FNAME:
// procedure name (f77 kludge): name,,NO_SECT,0,0
@@ -4130,38 +4089,9 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) {
::strstr(symbol_sect_name, "__objc") == symbol_sect_name) {
type = eSymbolTypeRuntime;
- if (symbol_name) {
- llvm::StringRef symbol_name_ref(symbol_name);
- if (symbol_name_ref.starts_with("_OBJC_")) {
- llvm::StringRef g_objc_v2_prefix_class(
- "_OBJC_CLASS_$_");
- llvm::StringRef g_objc_v2_prefix_metaclass(
- "_OBJC_METACLASS_$_");
- llvm::StringRef g_objc_v2_prefix_ivar(
- "_OBJC_IVAR_$_");
- if (symbol_name_ref.starts_with(g_objc_v2_prefix_class)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_class.size();
- type = eSymbolTypeObjCClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_metaclass)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_metaclass.size();
- type = eSymbolTypeObjCMetaClass;
- demangled_is_synthesized = true;
- } else if (symbol_name_ref.starts_with(
- g_objc_v2_prefix_ivar)) {
- symbol_name_non_abi_mangled = symbol_name + 1;
- symbol_name =
- symbol_name + g_objc_v2_prefix_ivar.size();
- type = eSymbolTypeObjCIVar;
- demangled_is_synthesized = true;
- }
- }
- }
+ if (TryParseV2ObjCMetadataSymbol(
+ symbol_name, symbol_name_non_abi_mangled, type))
+ demangled_is_synthesized = true;
} else if (symbol_sect_name &&
::strstr(symbol_sect_name, "__gcc_except_tab") ==
symbol_sect_name) {
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
index 600cc0a04cd2..57d88f615e2b 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.cpp
@@ -9,6 +9,7 @@
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
+#include "lldb/Host/HostInfo.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/UriParser.h"
@@ -477,6 +478,249 @@ std::string PlatformAndroid::GetRunAs() {
}
return run_as.str();
}
+
+// Helper function to populate process status information from
+// /proc/[pid]/status
+void PlatformAndroid::PopulateProcessStatusInfo(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ // Read /proc/[pid]/status to get parent PID, UIDs, and GIDs
+ Status error;
+ AdbClientUP status_adb = GetAdbClient(error);
+ if (error.Fail())
+ return;
+
+ std::string status_output;
+ StreamString status_cmd;
+ status_cmd.Printf(
+ "cat /proc/%llu/status 2>/dev/null | grep -E '^(PPid|Uid|Gid):'",
+ static_cast<unsigned long long>(pid));
+ Status status_error =
+ status_adb->Shell(status_cmd.GetData(), seconds(5), &status_output);
+
+ if (status_error.Fail() || status_output.empty())
+ return;
+
+ llvm::SmallVector<llvm::StringRef, 16> lines;
+ llvm::StringRef(status_output).split(lines, '\n');
+
+ for (llvm::StringRef line : lines) {
+ line = line.trim();
+ if (line.starts_with("PPid:")) {
+ llvm::StringRef ppid_str = line.substr(5).trim();
+ lldb::pid_t ppid;
+ if (llvm::to_integer(ppid_str, ppid))
+ process_info.SetParentProcessID(ppid);
+ } else if (line.starts_with("Uid:")) {
+ llvm::SmallVector<llvm::StringRef, 4> uid_parts;
+ line.substr(4).trim().split(uid_parts, '\t', -1, false);
+ if (uid_parts.size() >= 2) {
+ uint32_t uid, euid;
+ if (llvm::to_integer(uid_parts[0].trim(), uid))
+ process_info.SetUserID(uid);
+ if (llvm::to_integer(uid_parts[1].trim(), euid))
+ process_info.SetEffectiveUserID(euid);
+ }
+ } else if (line.starts_with("Gid:")) {
+ llvm::SmallVector<llvm::StringRef, 4> gid_parts;
+ line.substr(4).trim().split(gid_parts, '\t', -1, false);
+ if (gid_parts.size() >= 2) {
+ uint32_t gid, egid;
+ if (llvm::to_integer(gid_parts[0].trim(), gid))
+ process_info.SetGroupID(gid);
+ if (llvm::to_integer(gid_parts[1].trim(), egid))
+ process_info.SetEffectiveGroupID(egid);
+ }
+ }
+ }
+}
+
+// Helper function to populate command line arguments from /proc/[pid]/cmdline
+void PlatformAndroid::PopulateProcessCommandLine(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ // Read /proc/[pid]/cmdline to get command line arguments
+ Status error;
+ AdbClientUP cmdline_adb = GetAdbClient(error);
+ if (error.Fail())
+ return;
+
+ std::string cmdline_output;
+ StreamString cmdline_cmd;
+ cmdline_cmd.Printf("cat /proc/%llu/cmdline 2>/dev/null | tr '\\000' ' '",
+ static_cast<unsigned long long>(pid));
+ Status cmdline_error =
+ cmdline_adb->Shell(cmdline_cmd.GetData(), seconds(5), &cmdline_output);
+
+ if (cmdline_error.Fail() || cmdline_output.empty())
+ return;
+
+ cmdline_output = llvm::StringRef(cmdline_output).trim().str();
+ if (cmdline_output.empty())
+ return;
+
+ llvm::SmallVector<llvm::StringRef, 16> args;
+ llvm::StringRef(cmdline_output).split(args, ' ', -1, false);
+ if (args.empty())
+ return;
+
+ process_info.SetArg0(args[0]);
+ Args process_args;
+ for (size_t i = 1; i < args.size(); i++) {
+ if (!args[i].empty())
+ process_args.AppendArgument(args[i]);
+ }
+ process_info.SetArguments(process_args, false);
+}
+
+// Helper function to populate architecture from /proc/[pid]/exe
+void PlatformAndroid::PopulateProcessArchitecture(
+ lldb::pid_t pid, ProcessInstanceInfo &process_info) {
+ // Read /proc/[pid]/exe to get executable path for architecture detection
+ Status error;
+ AdbClientUP exe_adb = GetAdbClient(error);
+ if (error.Fail())
+ return;
+
+ std::string exe_output;
+ StreamString exe_cmd;
+ exe_cmd.Printf("readlink /proc/%llu/exe 2>/dev/null",
+ static_cast<unsigned long long>(pid));
+ Status exe_error = exe_adb->Shell(exe_cmd.GetData(), seconds(5), &exe_output);
+
+ if (exe_error.Fail() || exe_output.empty())
+ return;
+
+ exe_output = llvm::StringRef(exe_output).trim().str();
+
+ // Determine architecture from exe path
+ ArchSpec arch;
+ if (exe_output.find("64") != std::string::npos ||
+ exe_output.find("arm64") != std::string::npos ||
+ exe_output.find("aarch64") != std::string::npos) {
+ arch.SetTriple("aarch64-unknown-linux-android");
+ } else if (exe_output.find("x86_64") != std::string::npos) {
+ arch.SetTriple("x86_64-unknown-linux-android");
+ } else if (exe_output.find("x86") != std::string::npos ||
+ exe_output.find("i686") != std::string::npos) {
+ arch.SetTriple("i686-unknown-linux-android");
+ } else {
+ // Default to armv7 for 32-bit ARM (most common on Android)
+ arch.SetTriple("armv7-unknown-linux-android");
+ }
+
+ if (arch.IsValid())
+ process_info.SetArchitecture(arch);
+}
+
+uint32_t
+PlatformAndroid::FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &proc_infos) {
+ proc_infos.clear();
+
+ // When LLDB is running natively on an Android device (IsHost() == true),
+ // use the parent class's standard Linux /proc enumeration. IsHost() is only
+ // true when compiled for Android (#if defined(__ANDROID__)), so calling
+ // PlatformLinux methods is safe (Android is Linux-based).
+ if (IsHost())
+ return PlatformLinux::FindProcesses(match_info, proc_infos);
+
+ // Remote Android platform: implement process name lookup using 'pidof' over
+ // adb.
+
+ // LLDB stores the search name in GetExecutableFile() (even though it's
+ // actually a process name like "com.android.chrome" rather than an
+ // executable path). If no search name is provided, we can't use
+ // 'pidof', so return early with no results.
+ const ProcessInstanceInfo &match_process_info = match_info.GetProcessInfo();
+ if (!match_process_info.GetExecutableFile() ||
+ match_info.GetNameMatchType() == NameMatch::Ignore) {
+ return 0;
+ }
+
+ // Extract the process name to search for (typically an Android package name
+ // like "com.example.app" or binary name like "app_process64")
+ std::string process_name = match_process_info.GetExecutableFile().GetPath();
+ if (process_name.empty())
+ return 0;
+
+ // Use adb to find the process by name
+ Status error;
+ AdbClientUP adb(GetAdbClient(error));
+ if (error.Fail()) {
+ Log *log = GetLog(LLDBLog::Platform);
+ LLDB_LOGF(log, "PlatformAndroid::%s failed to get ADB client: %s",
+ __FUNCTION__, error.AsCString());
+ return 0;
+ }
+
+ // Use 'pidof' command to get PIDs for the process name.
+ // Quote the process name to handle special characters (spaces, etc.)
+ std::string pidof_output;
+ StreamString command;
+ command.Printf("pidof '%s'", process_name.c_str());
+ error = adb->Shell(command.GetData(), seconds(5), &pidof_output);
+
+ if (error.Fail()) {
+ Log *log = GetLog(LLDBLog::Platform);
+ LLDB_LOG(log, "PlatformAndroid::{} 'pidof {}' failed: {}", __FUNCTION__,
+ process_name.c_str(), error.AsCString());
+ return 0;
+ }
+
+ // Parse PIDs from pidof output.
+ // Note: pidof can return multiple PIDs (space-separated) if multiple
+ // instances of the same executable are running.
+ pidof_output = llvm::StringRef(pidof_output).trim().str();
+ if (pidof_output.empty()) {
+ Log *log = GetLog(LLDBLog::Platform);
+ LLDB_LOGF(log, "PlatformAndroid::%s no process found with name '%s'",
+ __FUNCTION__, process_name.c_str());
+ return 0;
+ }
+
+ // Split the output by whitespace to handle multiple PIDs
+ llvm::SmallVector<llvm::StringRef, 8> pid_strings;
+ llvm::StringRef(pidof_output).split(pid_strings, ' ', -1, false);
+
+ Log *log = GetLog(LLDBLog::Platform);
+
+ // Process each PID and gather information
+ uint32_t num_matches = 0;
+ for (llvm::StringRef pid_str : pid_strings) {
+ pid_str = pid_str.trim();
+ if (pid_str.empty())
+ continue;
+
+ lldb::pid_t pid;
+ if (!llvm::to_integer(pid_str, pid)) {
+ LLDB_LOGF(log, "PlatformAndroid::%s failed to parse PID from: '%s'",
+ __FUNCTION__, pid_str.str().c_str());
+ continue;
+ }
+
+ ProcessInstanceInfo process_info;
+ process_info.SetProcessID(pid);
+ process_info.GetExecutableFile().SetFile(process_name,
+ FileSpec::Style::posix);
+
+ // Populate additional process information
+ PopulateProcessStatusInfo(pid, process_info);
+ PopulateProcessCommandLine(pid, process_info);
+ PopulateProcessArchitecture(pid, process_info);
+
+ // Check if this process matches the criteria
+ if (match_info.Matches(process_info)) {
+ proc_infos.push_back(process_info);
+ num_matches++;
+
+ LLDB_LOGF(log, "PlatformAndroid::%s found process '%s' with PID %llu",
+ __FUNCTION__, process_name.c_str(),
+ static_cast<unsigned long long>(pid));
+ }
+ }
+
+ return num_matches;
+}
+
std::unique_ptr<AdbSyncService> PlatformAndroid::GetSyncService(Status &error) {
auto sync_service = std::make_unique<AdbSyncService>(m_device_id);
error = sync_service->SetupSyncConnection();
diff --git a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
index 3384525362ec..e771c6ae97d4 100644
--- a/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
+++ b/lldb/source/Plugins/Platform/Android/PlatformAndroid.h
@@ -59,6 +59,9 @@ public:
uint32_t GetDefaultMemoryCacheLineSize() override;
+ uint32_t FindProcesses(const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &proc_infos) override;
+
protected:
const char *GetCacheHostname() override;
@@ -86,6 +89,14 @@ protected:
private:
std::string m_device_id;
uint32_t m_sdk_version;
+
+ // Helper functions for process information gathering
+ void PopulateProcessStatusInfo(lldb::pid_t pid,
+ ProcessInstanceInfo &process_info);
+ void PopulateProcessCommandLine(lldb::pid_t pid,
+ ProcessInstanceInfo &process_info);
+ void PopulateProcessArchitecture(lldb::pid_t pid,
+ ProcessInstanceInfo &process_info);
};
} // namespace platform_android
diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index cd72454fe028..5aad4470091b 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -1150,7 +1150,7 @@ void PlatformDarwin::AddClangModuleCompilationOptionsForSDKType(
case XcodeSDK::Type::XRSimulator:
case XcodeSDK::Type::XROS:
// FIXME: Pass the right argument once it exists.
- case XcodeSDK::Type::bridgeOS:
+ case XcodeSDK::Type::BridgeOS:
case XcodeSDK::Type::Linux:
case XcodeSDK::Type::unknown:
if (Log *log = GetLog(LLDBLog::Host)) {
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 9c798cb1cc8f..7ef50da3641b 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -218,8 +218,8 @@ static Status EnsureFDFlags(int fd, int flags) {
static llvm::Error AddPtraceScopeNote(llvm::Error original_error) {
Expected<int> ptrace_scope = GetPtraceScope();
if (auto E = ptrace_scope.takeError()) {
- Log *log = GetLog(POSIXLog::Process);
- LLDB_LOG(log, "error reading value of ptrace_scope: {0}", E);
+ LLDB_LOG_ERROR(GetLog(POSIXLog::Process), std::move(E),
+ "error reading value of ptrace_scope: {0}");
// The original error is probably more interesting than not being able to
// read or interpret ptrace_scope.
@@ -230,6 +230,7 @@ static llvm::Error AddPtraceScopeNote(llvm::Error original_error) {
switch (*ptrace_scope) {
case 1:
case 2:
+ llvm::consumeError(std::move(original_error));
return llvm::createStringError(
std::error_code(errno, std::generic_category()),
"The current value of ptrace_scope is %d, which can cause ptrace to "
@@ -239,6 +240,7 @@ static llvm::Error AddPtraceScopeNote(llvm::Error original_error) {
"https://www.kernel.org/doc/Documentation/security/Yama.txt.",
*ptrace_scope);
case 3:
+ llvm::consumeError(std::move(original_error));
return llvm::createStringError(
std::error_code(errno, std::generic_category()),
"The current value of ptrace_scope is 3, which will cause ptrace to "
diff --git a/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp b/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
index 15981a2c1cb8..a8d18f774c36 100644
--- a/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
+++ b/lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp
@@ -47,7 +47,7 @@ void GDBRemoteSignals::Reset() {
AddSignal(25, "SIGXFSZ", false, true, true, "file size limit exceeded");
AddSignal(26, "SIGVTALRM", false, true, true, "virtual time alarm");
AddSignal(27, "SIGPROF", false, false, false, "profiling time alarm");
- AddSignal(28, "SIGWINCH", false, true, true, "window size changes");
+ AddSignal(28, "SIGWINCH", false, false, false, "window size changes");
AddSignal(29, "SIGLOST", false, true, true, "resource lost");
AddSignal(30, "SIGUSR1", false, true, true, "user defined signal 1");
AddSignal(31, "SIGUSR2", false, true, true, "user defined signal 2");
diff --git a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
index 5346babc1857..dbbfc6a352e0 100644
--- a/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
+++ b/lldb/source/Plugins/Process/Utility/LinuxSignals.cpp
@@ -160,7 +160,7 @@ void LinuxSignals::Reset() {
ADD_LINUX_SIGNAL(25, "SIGXFSZ", false, true, true, "file size limit exceeded");
ADD_LINUX_SIGNAL(26, "SIGVTALRM", false, true, true, "virtual time alarm");
ADD_LINUX_SIGNAL(27, "SIGPROF", false, false, false, "profiling time alarm");
- ADD_LINUX_SIGNAL(28, "SIGWINCH", false, true, true, "window size changes");
+ ADD_LINUX_SIGNAL(28, "SIGWINCH", false, false, false, "window size changes");
ADD_LINUX_SIGNAL(29, "SIGIO", false, true, true, "input/output ready/Pollable event", "SIGPOLL");
ADD_LINUX_SIGNAL(30, "SIGPWR", false, true, true, "power failure");
ADD_LINUX_SIGNAL(31, "SIGSYS", false, true, true, "invalid system call");
diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
index e0f3971c6e27..c361b2abb726 100644
--- a/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
+++ b/lldb/source/Plugins/Process/Utility/RegisterContextFreeBSD_x86_64.cpp
@@ -9,6 +9,7 @@
#include "RegisterContextFreeBSD_x86_64.h"
#include "RegisterContextFreeBSD_i386.h"
#include "RegisterContextPOSIX_x86.h"
+#include "llvm/Support/Threading.h"
#include <vector>
using namespace lldb_private;
@@ -69,40 +70,34 @@ struct UserArea {
#include "RegisterInfos_x86_64.h"
#undef DECLARE_REGISTER_INFOS_X86_64_STRUCT
-static std::vector<lldb_private::RegisterInfo> &GetSharedRegisterInfoVector() {
- static std::vector<lldb_private::RegisterInfo> register_infos;
- return register_infos;
-}
-
-static const RegisterInfo *
-GetRegisterInfo_i386(const lldb_private::ArchSpec &arch) {
- static std::vector<lldb_private::RegisterInfo> g_register_infos(
- GetSharedRegisterInfoVector());
-
- // Allocate RegisterInfo only once
- if (g_register_infos.empty()) {
- // Copy the register information from base class
- std::unique_ptr<RegisterContextFreeBSD_i386> reg_interface(
- new RegisterContextFreeBSD_i386(arch));
- const RegisterInfo *base_info = reg_interface->GetRegisterInfo();
- g_register_infos.insert(g_register_infos.end(), &base_info[0],
- &base_info[k_num_registers_i386]);
+static std::vector<lldb_private::RegisterInfo> &
+GetSharedRegisterInfoVector_i386(const lldb_private::ArchSpec &arch) {
+ static std::vector<lldb_private::RegisterInfo> g_register_infos;
+ static llvm::once_flag g_initialized;
+ llvm::call_once(g_initialized, [&]() {
+ if (g_register_infos.empty()) {
+ // Copy the register information from base class
+ std::unique_ptr<RegisterContextFreeBSD_i386> reg_interface(
+ new RegisterContextFreeBSD_i386(arch));
+ const RegisterInfo *base_info = reg_interface->GetRegisterInfo();
+ g_register_infos.insert(g_register_infos.end(), &base_info[0],
+ &base_info[k_num_registers_i386]);
// Include RegisterInfos_x86_64 to update the g_register_infos structure
// with x86_64 offsets.
#define UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
#include "RegisterInfos_x86_64.h"
#undef UPDATE_REGISTER_INFOS_I386_STRUCT_WITH_X86_64_OFFSETS
- }
-
- return &g_register_infos[0];
+ }
+ });
+ return g_register_infos;
}
static const RegisterInfo *
PrivateGetRegisterInfoPtr(const lldb_private::ArchSpec &target_arch) {
switch (target_arch.GetMachine()) {
case llvm::Triple::x86:
- return GetRegisterInfo_i386(target_arch);
+ return &GetSharedRegisterInfoVector_i386(target_arch)[0];
case llvm::Triple::x86_64:
return g_register_infos_x86_64;
default:
@@ -116,9 +111,10 @@ PrivateGetRegisterCount(const lldb_private::ArchSpec &target_arch) {
switch (target_arch.GetMachine()) {
case llvm::Triple::x86:
// This vector should have already been filled.
- assert(!GetSharedRegisterInfoVector().empty() &&
+ assert(!GetSharedRegisterInfoVector_i386(target_arch).empty() &&
"i386 register info vector not filled.");
- return static_cast<uint32_t>(GetSharedRegisterInfoVector().size());
+ return static_cast<uint32_t>(
+ GetSharedRegisterInfoVector_i386(target_arch).size());
case llvm::Triple::x86_64:
return static_cast<uint32_t>(sizeof(g_register_infos_x86_64) /
sizeof(g_register_infos_x86_64[0]));
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index 7d2bd452acca..11f164c2426c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -211,6 +211,12 @@ bool GDBRemoteCommunicationClient::GetReverseStepSupported() {
return m_supports_reverse_step == eLazyBoolYes;
}
+bool GDBRemoteCommunicationClient::GetMultiMemReadSupported() {
+ if (m_supports_multi_mem_read == eLazyBoolCalculate)
+ GetRemoteQSupported();
+ return m_supports_multi_mem_read == eLazyBoolYes;
+}
+
bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
if (m_supports_not_sending_acks == eLazyBoolCalculate) {
m_send_acks = true;
@@ -339,6 +345,7 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
m_supported_async_json_packets_is_valid = false;
m_supported_async_json_packets_sp.reset();
m_supports_jModulesInfo = true;
+ m_supports_multi_mem_read = eLazyBoolCalculate;
}
// These flags should be reset when we first connect to a GDB server and when
@@ -365,6 +372,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_x_packet_state.reset();
m_supports_reverse_continue = eLazyBoolNo;
m_supports_reverse_step = eLazyBoolNo;
+ m_supports_multi_mem_read = eLazyBoolNo;
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
// not, we assume no limit
@@ -424,6 +432,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_reverse_continue = eLazyBoolYes;
else if (x == "ReverseStep+")
m_supports_reverse_step = eLazyBoolYes;
+ else if (x == "MultiMemRead+")
+ m_supports_multi_mem_read = eLazyBoolYes;
// Look for a list of compressions in the features list e.g.
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
// deflate,lzma
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index a765e95bf981..ad590a25d0f1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -342,6 +342,8 @@ public:
bool GetReverseStepSupported();
+ bool GetMultiMemReadSupported();
+
LazyBool SupportsAllocDeallocMemory() // const
{
// Uncomment this to have lldb pretend the debug server doesn't respond to
@@ -574,6 +576,7 @@ protected:
std::optional<xPacketState> m_x_packet_state;
LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
LazyBool m_supports_reverse_step = eLazyBoolCalculate;
+ LazyBool m_supports_multi_mem_read = eLazyBoolCalculate;
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
m_supports_qUserName : 1, m_supports_qGroupName : 1,
diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
index b4681718cb68..2e0207696a0e 100644
--- a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp
@@ -22,7 +22,7 @@ using namespace lldb_private::process_gdb_remote;
using namespace lldb_private::wasm;
RegisterContextWasm::RegisterContextWasm(
- wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+ ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
GDBRemoteDynamicRegisterInfoSP reg_info_sp)
: GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
false) {}
diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
index 7e63eb85bc75..6ca31e530d14 100644
--- a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
+++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h
@@ -10,6 +10,7 @@
#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H
#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h"
+#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
#include "ThreadWasm.h"
#include "Utility/WasmVirtualRegisters.h"
#include "lldb/lldb-private-types.h"
@@ -34,7 +35,7 @@ class RegisterContextWasm
: public process_gdb_remote::GDBRemoteRegisterContext {
public:
RegisterContextWasm(
- wasm::ThreadWasm &thread, uint32_t concrete_frame_idx,
+ process_gdb_remote::ThreadGDBRemote &thread, uint32_t concrete_frame_idx,
process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp);
~RegisterContextWasm() override;
diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
index 99845dd2918d..319c5e262aaf 100644
--- a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
@@ -9,6 +9,7 @@
#include "UnwindWasm.h"
#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
#include "ProcessWasm.h"
+#include "RegisterContextWasm.h"
#include "ThreadWasm.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
@@ -18,21 +19,6 @@ using namespace lldb_private;
using namespace process_gdb_remote;
using namespace wasm;
-class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext {
-public:
- WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread,
- uint32_t concrete_frame_idx,
- GDBRemoteDynamicRegisterInfoSP &reg_info_sp,
- uint64_t pc)
- : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
- false) {
- // Wasm does not have a fixed set of registers but relies on a mechanism
- // named local and global variables to store information such as the stack
- // pointer. The only actual register is the PC.
- PrivateSetRegisterValue(0, pc);
- }
-};
-
lldb::RegisterContextSP
UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
if (m_frames.size() <= frame->GetFrameIndex())
@@ -43,9 +29,9 @@ UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
ProcessWasm *wasm_process =
static_cast<ProcessWasm *>(thread->GetProcess().get());
- return std::make_shared<WasmGDBRemoteRegisterContext>(
- *gdb_thread, frame->GetConcreteFrameIndex(),
- wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]);
+ return std::make_shared<RegisterContextWasm>(*gdb_thread,
+ frame->GetConcreteFrameIndex(),
+ wasm_process->GetRegisterInfo());
}
uint32_t UnwindWasm::DoGetFrameCount() {
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
index d7293fc28c52..390cf3eeb16a 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp
@@ -52,11 +52,6 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() {
}
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>("command", "Run an lldb command."));
server.AddTool(std::make_unique<DebuggerListTool>(
@@ -74,26 +69,9 @@ void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) {
io_sp, io_sp, [client_name](llvm::StringRef message) {
LLDB_LOG(GetLog(LLDBLog::Host), "{0}: {1}", client_name, message);
});
- MCPTransport *transport_ptr = transport_up.get();
- auto instance_up = std::make_unique<lldb_protocol::mcp::Server>(
- std::string(kName), std::string(kVersion), *transport_up,
- /*log_callback=*/
- [client_name](llvm::StringRef message) {
- LLDB_LOG(GetLog(LLDBLog::Host), "{0} Server: {1}", client_name,
- message);
- },
- /*closed_callback=*/
- [this, transport_ptr]() { m_instances.erase(transport_ptr); });
- Extend(*instance_up);
- llvm::Expected<MainLoop::ReadHandleUP> handle =
- transport_up->RegisterMessageHandler(m_loop, *instance_up);
- if (!handle) {
- LLDB_LOG_ERROR(log, handle.takeError(), "Failed to run MCP server: {0}");
- return;
- }
- m_instances[transport_ptr] =
- std::make_tuple<ServerUP, ReadHandleUP, TransportUP>(
- std::move(instance_up), std::move(*handle), std::move(transport_up));
+
+ if (auto error = m_server->Accept(m_loop, std::move(transport_up)))
+ LLDB_LOG_ERROR(log, std::move(error), "{0}:");
}
llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
@@ -124,14 +102,21 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) {
llvm::join(m_listener->GetListeningConnectionURI(), ", ");
ServerInfo info{listening_uris[0]};
- llvm::Expected<ServerInfoHandle> handle = ServerInfo::Write(info);
- if (!handle)
- return handle.takeError();
+ llvm::Expected<ServerInfoHandle> server_info_handle = ServerInfo::Write(info);
+ if (!server_info_handle)
+ return server_info_handle.takeError();
+
+ m_client_count = 0;
+ m_server = std::make_unique<lldb_protocol::mcp::Server>(
+ std::string(kName), std::string(kVersion), [](StringRef message) {
+ LLDB_LOG(GetLog(LLDBLog::Host), "MCP Server: {0}", message);
+ });
+ Extend(*m_server);
m_running = true;
- m_server_info_handle = std::move(*handle);
- m_listen_handlers = std::move(*handles);
- m_loop_thread = std::thread([=] {
+ m_server_info_handle = std::move(*server_info_handle);
+ m_accept_handles = std::move(*handles);
+ m_loop_thread = std::thread([this] {
llvm::set_thread_name("protocol-server.mcp");
m_loop.Run();
});
@@ -155,9 +140,11 @@ llvm::Error ProtocolServerMCP::Stop() {
if (m_loop_thread.joinable())
m_loop_thread.join();
+ m_accept_handles.clear();
+
+ m_server.reset(nullptr);
m_server_info_handle.Remove();
- m_listen_handlers.clear();
- m_instances.clear();
+ m_listener.reset();
return llvm::Error::success();
}
diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
index b325a3681bcc..e0f2a6ccea1f 100644
--- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
+++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h
@@ -23,16 +23,17 @@
namespace lldb_private::mcp {
class ProtocolServerMCP : public ProtocolServer {
- using ReadHandleUP = MainLoopBase::ReadHandleUP;
- using TransportUP = std::unique_ptr<lldb_protocol::mcp::MCPTransport>;
+
using ServerUP = std::unique_ptr<lldb_protocol::mcp::Server>;
+ using ReadHandleUP = MainLoop::ReadHandleUP;
+
public:
ProtocolServerMCP();
- virtual ~ProtocolServerMCP() override;
+ ~ProtocolServerMCP() override;
- virtual llvm::Error Start(ProtocolServer::Connection connection) override;
- virtual llvm::Error Stop() override;
+ llvm::Error Start(ProtocolServer::Connection connection) override;
+ llvm::Error Stop() override;
static void Initialize();
static void Terminate();
@@ -56,19 +57,18 @@ private:
bool m_running = false;
- lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
lldb_private::MainLoop m_loop;
std::thread m_loop_thread;
std::mutex m_mutex;
size_t m_client_count = 0;
std::unique_ptr<Socket> m_listener;
+ std::vector<ReadHandleUP> m_accept_handles;
- std::vector<ReadHandleUP> m_listen_handlers;
- std::map<lldb_protocol::mcp::MCPTransport *,
- std::tuple<ServerUP, ReadHandleUP, TransportUP>>
- m_instances;
+ ServerUP m_server;
+ lldb_protocol::mcp::ServerInfoHandle m_server_info_handle;
};
+
} // namespace lldb_private::mcp
#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
index 660edaa0191f..c9bb38d213c9 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.cpp
@@ -81,6 +81,32 @@ std::optional<std::string> ScriptedBreakpointPythonInterface::GetShortHelp() {
return obj->GetAsString()->GetValue().str();
}
+lldb::BreakpointLocationSP ScriptedBreakpointPythonInterface::WasHit(
+ lldb::StackFrameSP frame_sp, lldb::BreakpointLocationSP bp_loc_sp) {
+ Status py_error;
+ lldb::BreakpointLocationSP loc_sp = Dispatch<lldb::BreakpointLocationSP>(
+ "was_hit", py_error, frame_sp, bp_loc_sp);
+
+ if (py_error.Fail())
+ return bp_loc_sp;
+
+ return loc_sp;
+}
+
+std::optional<std::string>
+ScriptedBreakpointPythonInterface::GetLocationDescription(
+ lldb::BreakpointLocationSP bp_loc_sp, lldb::DescriptionLevel level) {
+ Status error;
+ StructuredData::ObjectSP obj =
+ Dispatch("get_location_description", error, bp_loc_sp, level);
+
+ if (!ScriptedInterface::CheckStructuredDataObject(LLVM_PRETTY_FUNCTION, obj,
+ error))
+ return {};
+
+ return obj->GetAsString()->GetValue().str();
+}
+
void ScriptedBreakpointPythonInterface::Initialize() {
const std::vector<llvm::StringRef> ci_usages = {
"breakpoint set -P classname [-k key -v value ...]"};
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h
index 27bdd8718ac0..72da0a195ee0 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedBreakpointPythonInterface.h
@@ -36,6 +36,12 @@ public:
bool ResolverCallback(SymbolContext sym_ctx) override;
lldb::SearchDepth GetDepth() override;
std::optional<std::string> GetShortHelp() override;
+ lldb::BreakpointLocationSP
+ WasHit(lldb::StackFrameSP frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp) override;
+ virtual std::optional<std::string>
+ GetLocationDescription(lldb::BreakpointLocationSP bp_loc_sp,
+ lldb::DescriptionLevel level) override;
static void Initialize();
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index 8083ccae0402..4fdf2b12a550 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -81,6 +81,19 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
}
template <>
+lldb::StackFrameSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>(
+ python::PythonObject &p, Status &error) {
+ if (lldb::SBFrame *sb_frame = reinterpret_cast<lldb::SBFrame *>(
+ python::LLDBSWIGPython_CastPyObjectToSBFrame(p.get())))
+ return m_interpreter.GetOpaqueTypeFromSBFrame(*sb_frame);
+ error = Status::FromErrorString(
+ "Couldn't cast lldb::SBFrame to lldb_private::StackFrame.");
+
+ return nullptr;
+}
+
+template <>
SymbolContext
ScriptedPythonInterface::ExtractValueFromPythonObject<SymbolContext>(
python::PythonObject &p, Status &error) {
@@ -127,6 +140,24 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
}
template <>
+lldb::BreakpointLocationSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<
+ lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error) {
+ lldb::SBBreakpointLocation *sb_break_loc =
+ reinterpret_cast<lldb::SBBreakpointLocation *>(
+ python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(p.get()));
+
+ if (!sb_break_loc) {
+ error = Status::FromErrorStringWithFormat(
+ "Couldn't cast lldb::SBBreakpointLocation to "
+ "lldb::BreakpointLocationSP.");
+ return nullptr;
+ }
+
+ return m_interpreter.GetOpaqueTypeFromSBBreakpointLocation(*sb_break_loc);
+}
+
+template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error) {
lldb::SBAttachInfo *sb_attach_info = reinterpret_cast<lldb::SBAttachInfo *>(
@@ -194,4 +225,22 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
return m_interpreter.GetOpaqueTypeFromSBExecutionContext(*sb_exe_ctx);
}
+template <>
+lldb::DescriptionLevel
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>(
+ python::PythonObject &p, Status &error) {
+ lldb::DescriptionLevel ret_val = lldb::eDescriptionLevelBrief;
+ llvm::Expected<unsigned long long> unsigned_or_err = p.AsUnsignedLongLong();
+ if (!unsigned_or_err) {
+ error = (Status::FromError(unsigned_or_err.takeError()));
+ return ret_val;
+ }
+ unsigned long long unsigned_val = *unsigned_or_err;
+ if (unsigned_val >= lldb::DescriptionLevel::kNumDescriptionLevels) {
+ error = Status("value too large for lldb::DescriptionLevel.");
+ return ret_val;
+ }
+ return static_cast<lldb::DescriptionLevel>(unsigned_val);
+}
+
#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index f769d3d29add..2335b2ef0f17 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -436,6 +436,10 @@ protected:
return python::SWIGBridge::ToSWIGWrapper(arg);
}
+ python::PythonObject Transform(lldb::BreakpointLocationSP arg) {
+ return python::SWIGBridge::ToSWIGWrapper(arg);
+ }
+
python::PythonObject Transform(lldb::ProcessSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
@@ -464,10 +468,18 @@ protected:
return python::SWIGBridge::ToSWIGWrapper(arg.get());
}
+ python::PythonObject Transform(lldb::StackFrameSP arg) {
+ return python::SWIGBridge::ToSWIGWrapper(arg);
+ }
+
python::PythonObject Transform(lldb::DataExtractorSP arg) {
return python::SWIGBridge::ToSWIGWrapper(arg);
}
+ python::PythonObject Transform(lldb::DescriptionLevel arg) {
+ return python::SWIGBridge::ToSWIGWrapper(arg);
+ }
+
template <typename T, typename U>
void ReverseTransform(T &original_arg, U transformed_arg, Status &error) {
// If U is not a PythonObject, don't touch it!
@@ -574,11 +586,21 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StreamSP>(
python::PythonObject &p, Status &error);
template <>
+lldb::StackFrameSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::StackFrameSP>(
+ python::PythonObject &p, Status &error);
+
+template <>
lldb::BreakpointSP
ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::BreakpointSP>(
python::PythonObject &p, Status &error);
template <>
+lldb::BreakpointLocationSP
+ScriptedPythonInterface::ExtractValueFromPythonObject<
+ lldb::BreakpointLocationSP>(python::PythonObject &p, Status &error);
+
+template <>
lldb::ProcessAttachInfoSP ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ProcessAttachInfoSP>(python::PythonObject &p, Status &error);
@@ -601,6 +623,11 @@ lldb::ExecutionContextRefSP
ScriptedPythonInterface::ExtractValueFromPythonObject<
lldb::ExecutionContextRefSP>(python::PythonObject &p, Status &error);
+template <>
+lldb::DescriptionLevel
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::DescriptionLevel>(
+ python::PythonObject &p, Status &error);
+
} // namespace lldb_private
#endif // LLDB_ENABLE_PYTHON
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
index 413778639c29..7b39d29ba2b2 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h
@@ -107,6 +107,7 @@ public:
static PythonObject ToSWIGWrapper(lldb::ProcessAttachInfoSP attach_info_sp);
static PythonObject ToSWIGWrapper(lldb::ProcessLaunchInfoSP launch_info_sp);
static PythonObject ToSWIGWrapper(lldb::DataExtractorSP data_extractor_sp);
+ static PythonObject ToSWIGWrapper(lldb::DescriptionLevel level);
static PythonObject
ToSWIGWrapper(std::unique_ptr<lldb::SBStructuredData> data_sb);
@@ -256,11 +257,13 @@ public:
void *LLDBSWIGPython_CastPyObjectToSBData(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBBreakpoint(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBAttachInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBLaunchInfo(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBError(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBEvent(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data);
+void *LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBSymbolContext(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data);
void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data);
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index f1e73d73a733..82e9d867c3ac 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -3126,43 +3126,6 @@ void DWARFASTParserClang::ParseSingleMember(
if (!member_clang_type.IsCompleteType())
member_clang_type.GetCompleteType();
- {
- // Older versions of clang emit the same DWARF for array[0] and array[1]. If
- // the current field is at the end of the structure, then there is
- // definitely no room for extra elements and we override the type to
- // array[0]. This was fixed by f454dfb6b5af.
- CompilerType member_array_element_type;
- uint64_t member_array_size;
- bool member_array_is_incomplete;
-
- if (member_clang_type.IsArrayType(&member_array_element_type,
- &member_array_size,
- &member_array_is_incomplete) &&
- !member_array_is_incomplete) {
- uint64_t parent_byte_size =
- parent_die.GetAttributeValueAsUnsigned(DW_AT_byte_size, UINT64_MAX);
-
- // If the attrs.member_byte_offset is still set to UINT32_MAX this means
- // that the DW_TAG_member didn't have a DW_AT_data_member_location, so
- // don't emit an error if this is the case.
- if (attrs.member_byte_offset != UINT32_MAX &&
- attrs.member_byte_offset >= parent_byte_size) {
- if (member_array_size != 1 &&
- (member_array_size != 0 ||
- attrs.member_byte_offset > parent_byte_size)) {
- module_sp->ReportError(
- "{0:x8}: DW_TAG_member '{1}' refers to type {2:x16}"
- " which extends beyond the bounds of {3:x8}",
- die.GetID(), attrs.name,
- attrs.encoding_form.Reference().GetOffset(), parent_die.GetID());
- }
-
- member_clang_type =
- m_ast.CreateArrayType(member_array_element_type, 0, false);
- }
- }
- }
-
TypeSystemClang::RequireCompleteType(member_clang_type);
clang::FieldDecl *field_decl = TypeSystemClang::AddFieldToRecordType(
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
index 51bdcc92b05a..e7fddf08967f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp
@@ -38,16 +38,18 @@ struct CreateMethodDecl : public TypeVisitorCallbacks {
TypeIndex func_type_index,
clang::FunctionDecl *&function_decl,
lldb::opaque_compiler_type_t parent_ty,
- llvm::StringRef proc_name, CompilerType func_ct)
+ llvm::StringRef proc_name, ConstString mangled_name,
+ CompilerType func_ct)
: m_index(m_index), m_clang(m_clang), func_type_index(func_type_index),
function_decl(function_decl), parent_ty(parent_ty),
- proc_name(proc_name), func_ct(func_ct) {}
+ proc_name(proc_name), mangled_name(mangled_name), func_ct(func_ct) {}
PdbIndex &m_index;
TypeSystemClang &m_clang;
TypeIndex func_type_index;
clang::FunctionDecl *&function_decl;
lldb::opaque_compiler_type_t parent_ty;
llvm::StringRef proc_name;
+ ConstString mangled_name;
CompilerType func_ct;
llvm::Error visitKnownMember(CVMemberRecord &cvr,
@@ -87,8 +89,7 @@ struct CreateMethodDecl : public TypeVisitorCallbacks {
bool is_artificial = (options & MethodOptions::CompilerGenerated) ==
MethodOptions::CompilerGenerated;
function_decl = m_clang.AddMethodToCXXRecordType(
- parent_ty, proc_name,
- /*asm_label=*/{}, func_ct, /*access=*/access_type,
+ parent_ty, proc_name, mangled_name, func_ct, /*access=*/access_type,
/*is_virtual=*/is_virtual, /*is_static=*/is_static,
/*is_inline=*/false, /*is_explicit=*/false,
/*is_attr_used=*/false, /*is_artificial=*/is_artificial);
@@ -892,6 +893,10 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id,
tag_record = CVTagRecord::create(index.tpi().getType(*eti)).asTag();
}
}
+
+ ConstString mangled_name(
+ pdb->FindMangledFunctionName(func_id).value_or(llvm::StringRef()));
+
if (!tag_record.FieldList.isSimple()) {
CVType field_list_cvt = index.tpi().getType(tag_record.FieldList);
FieldListRecord field_list;
@@ -899,15 +904,15 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id,
field_list_cvt, field_list))
llvm::consumeError(std::move(error));
CreateMethodDecl process(index, m_clang, func_ti, function_decl,
- parent_opaque_ty, func_name, func_ct);
+ parent_opaque_ty, func_name, mangled_name,
+ func_ct);
if (llvm::Error err = visitMemberRecordStream(field_list.Data, process))
llvm::consumeError(std::move(err));
}
if (!function_decl) {
function_decl = m_clang.AddMethodToCXXRecordType(
- parent_opaque_ty, func_name,
- /*asm_label=*/{}, func_ct,
+ parent_opaque_ty, func_name, mangled_name, func_ct,
/*access=*/lldb::AccessType::eAccessPublic,
/*is_virtual=*/false, /*is_static=*/false,
/*is_inline=*/false, /*is_explicit=*/false,
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 888bd89a7262..6c66d86d3e64 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -946,17 +946,21 @@ lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
case SimpleTypeKind::Complex64:
return lldb::eBasicTypeDoubleComplex;
case SimpleTypeKind::Complex32:
+ case SimpleTypeKind::Complex32PartialPrecision:
return lldb::eBasicTypeFloatComplex;
- case SimpleTypeKind::Float128:
case SimpleTypeKind::Float80:
return lldb::eBasicTypeLongDouble;
+ case SimpleTypeKind::Float128:
+ return lldb::eBasicTypeFloat128;
case SimpleTypeKind::Float64:
return lldb::eBasicTypeDouble;
case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Float32PartialPrecision:
return lldb::eBasicTypeFloat;
case SimpleTypeKind::Float16:
return lldb::eBasicTypeHalf;
case SimpleTypeKind::Int128:
+ case SimpleTypeKind::Int128Oct:
return lldb::eBasicTypeInt128;
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
@@ -967,6 +971,7 @@ lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
case SimpleTypeKind::Int16Short:
return lldb::eBasicTypeShort;
case SimpleTypeKind::UInt128:
+ case SimpleTypeKind::UInt128Oct:
return lldb::eBasicTypeUnsignedInt128;
case SimpleTypeKind::UInt64:
case SimpleTypeKind::UInt64Quad:
@@ -985,16 +990,27 @@ lldb_private::npdb::GetCompilerTypeForSimpleKind(SimpleTypeKind kind) {
return lldb::eBasicTypeVoid;
case SimpleTypeKind::WideCharacter:
return lldb::eBasicTypeWChar;
- default:
+
+ // Not supported.
+ case SimpleTypeKind::Float48:
+ case SimpleTypeKind::Complex16:
+ case SimpleTypeKind::Complex48:
+ case SimpleTypeKind::Complex128:
+ case SimpleTypeKind::NotTranslated:
+ case SimpleTypeKind::None:
return lldb::eBasicTypeInvalid;
}
+ return lldb::eBasicTypeInvalid;
}
size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
switch (kind) {
case SimpleTypeKind::Boolean128:
+ case SimpleTypeKind::Complex128:
case SimpleTypeKind::Int128:
+ case SimpleTypeKind::Int128Oct:
case SimpleTypeKind::UInt128:
+ case SimpleTypeKind::UInt128Oct:
case SimpleTypeKind::Float128:
return 16;
case SimpleTypeKind::Complex80:
@@ -1008,10 +1024,15 @@ size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
return 8;
+ case SimpleTypeKind::Complex48:
+ case SimpleTypeKind::Float48:
+ return 6;
case SimpleTypeKind::Boolean32:
case SimpleTypeKind::Character32:
case SimpleTypeKind::Complex32:
+ case SimpleTypeKind::Complex32PartialPrecision:
case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Float32PartialPrecision:
case SimpleTypeKind::Int32:
case SimpleTypeKind::Int32Long:
case SimpleTypeKind::UInt32Long:
@@ -1020,6 +1041,7 @@ size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
return 4;
case SimpleTypeKind::Boolean16:
case SimpleTypeKind::Character16:
+ case SimpleTypeKind::Complex16:
case SimpleTypeKind::Float16:
case SimpleTypeKind::Int16:
case SimpleTypeKind::Int16Short:
@@ -1035,10 +1057,13 @@ size_t lldb_private::npdb::GetTypeSizeForSimpleKind(SimpleTypeKind kind) {
case SimpleTypeKind::SByte:
case SimpleTypeKind::Character8:
return 1;
+
case SimpleTypeKind::Void:
- default:
+ case SimpleTypeKind::None:
+ case SimpleTypeKind::NotTranslated:
return 0;
}
+ return 0;
}
PdbTypeSymId lldb_private::npdb::GetBestPossibleDecl(PdbTypeSymId id,
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 8b3d775afc16..ecd3188b3d56 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -152,14 +152,24 @@ static bool IsFunctionEpilogue(const CompilandIndexItem &cci,
return false;
}
+// See llvm::codeview::TypeIndex::simpleTypeName as well as strForPrimitiveTi
+// from the original pdbdump:
+// https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/pdbdump/pdbdump.cpp#L1896-L1974
+//
+// For 64bit integers we use "long long" like DIA instead of "__int64".
static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
switch (kind) {
case SimpleTypeKind::Boolean128:
- case SimpleTypeKind::Boolean16:
- case SimpleTypeKind::Boolean32:
+ return "__bool128";
case SimpleTypeKind::Boolean64:
+ return "__bool64";
+ case SimpleTypeKind::Boolean32:
+ return "__bool32";
+ case SimpleTypeKind::Boolean16:
+ return "__bool16";
case SimpleTypeKind::Boolean8:
return "bool";
+
case SimpleTypeKind::Byte:
case SimpleTypeKind::UnsignedCharacter:
return "unsigned char";
@@ -168,57 +178,81 @@ static llvm::StringRef GetSimpleTypeName(SimpleTypeKind kind) {
case SimpleTypeKind::SignedCharacter:
case SimpleTypeKind::SByte:
return "signed char";
- case SimpleTypeKind::Character16:
- return "char16_t";
case SimpleTypeKind::Character32:
return "char32_t";
+ case SimpleTypeKind::Character16:
+ return "char16_t";
case SimpleTypeKind::Character8:
return "char8_t";
+
+ case SimpleTypeKind::Complex128:
+ return "_Complex __float128";
case SimpleTypeKind::Complex80:
+ return "_Complex long double";
case SimpleTypeKind::Complex64:
+ return "_Complex double";
+ case SimpleTypeKind::Complex48:
+ return "_Complex __float48";
case SimpleTypeKind::Complex32:
- return "complex";
+ case SimpleTypeKind::Complex32PartialPrecision:
+ return "_Complex float";
+ case SimpleTypeKind::Complex16:
+ return "_Complex _Float16";
+
case SimpleTypeKind::Float128:
+ return "__float128";
case SimpleTypeKind::Float80:
return "long double";
case SimpleTypeKind::Float64:
return "double";
+ case SimpleTypeKind::Float48:
+ return "__float48";
case SimpleTypeKind::Float32:
+ case SimpleTypeKind::Float32PartialPrecision:
return "float";
case SimpleTypeKind::Float16:
- return "single";
+ return "_Float16";
+
+ case SimpleTypeKind::Int128Oct:
case SimpleTypeKind::Int128:
return "__int128";
case SimpleTypeKind::Int64:
case SimpleTypeKind::Int64Quad:
- return "int64_t";
+ return "long long";
+ case SimpleTypeKind::Int32Long:
+ return "long";
case SimpleTypeKind::Int32:
return "int";
case SimpleTypeKind::Int16:
+ case SimpleTypeKind::Int16Short:
return "short";
+
+ case SimpleTypeKind::UInt128Oct:
case SimpleTypeKind::UInt128:
return "unsigned __int128";
case SimpleTypeKind::UInt64:
case SimpleTypeKind::UInt64Quad:
- return "uint64_t";
- case SimpleTypeKind::HResult:
- return "HRESULT";
+ return "unsigned long long";
case SimpleTypeKind::UInt32:
return "unsigned";
case SimpleTypeKind::UInt16:
case SimpleTypeKind::UInt16Short:
return "unsigned short";
- case SimpleTypeKind::Int32Long:
- return "long";
case SimpleTypeKind::UInt32Long:
return "unsigned long";
+
+ case SimpleTypeKind::HResult:
+ return "HRESULT";
case SimpleTypeKind::Void:
return "void";
case SimpleTypeKind::WideCharacter:
return "wchar_t";
- default:
+
+ case SimpleTypeKind::None:
+ case SimpleTypeKind::NotTranslated:
return "";
}
+ return "";
}
static bool IsClassRecord(TypeLeafKind kind) {
@@ -501,7 +535,11 @@ lldb::FunctionSP SymbolFileNativePDB::CreateFunction(PdbCompilandSymId func_id,
return nullptr;
PdbTypeSymId sig_id(proc.FunctionType, false);
- Mangled mangled(proc.Name);
+
+ std::optional<llvm::StringRef> mangled_opt = FindMangledSymbol(
+ SegmentOffset(proc.Segment, proc.CodeOffset), proc.FunctionType);
+ Mangled mangled(mangled_opt.value_or(proc.Name));
+
FunctionSP func_sp = std::make_shared<Function>(
&comp_unit, toOpaqueUid(func_id), toOpaqueUid(sig_id), mangled,
func_type.get(), func_addr,
@@ -594,8 +632,8 @@ lldb::TypeSP SymbolFileNativePDB::CreateSimpleType(TypeIndex ti,
uint64_t uid = toOpaqueUid(PdbTypeSymId(ti, false));
if (ti == TypeIndex::NullptrT()) {
Declaration decl;
- return MakeType(uid, ConstString("std::nullptr_t"), 0, nullptr,
- LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct,
+ return MakeType(uid, ConstString("decltype(nullptr)"), std::nullopt,
+ nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct,
Type::ResolveState::Full);
}
@@ -2137,14 +2175,17 @@ TypeSP SymbolFileNativePDB::CreateTypedef(PdbGlobalSymId id) {
if (!ts)
return nullptr;
- ts->GetNativePDBParser()->GetOrCreateTypedefDecl(id);
+ auto *typedef_decl = ts->GetNativePDBParser()->GetOrCreateTypedefDecl(id);
+
+ CompilerType ct = target_type->GetForwardCompilerType();
+ if (auto *clang = llvm::dyn_cast_or_null<TypeSystemClang>(ts.get()))
+ ct = clang->GetType(clang->getASTContext().getTypeDeclType(typedef_decl));
Declaration decl;
return MakeType(toOpaqueUid(id), ConstString(udt.Name),
llvm::expectedToOptional(target_type->GetByteSize(nullptr)),
nullptr, target_type->GetID(),
- lldb_private::Type::eEncodingIsTypedefUID, decl,
- target_type->GetForwardCompilerType(),
+ lldb_private::Type::eEncodingIsTypedefUID, decl, ct,
lldb_private::Type::ResolveState::Forward);
}
@@ -2662,6 +2703,83 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
return ctx;
}
+std::optional<llvm::StringRef>
+SymbolFileNativePDB::FindMangledFunctionName(PdbCompilandSymId func_id) {
+ const CompilandIndexItem *cci =
+ m_index->compilands().GetCompiland(func_id.modi);
+ if (!cci)
+ return std::nullopt;
+
+ CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(func_id.offset);
+ if (sym_record.kind() != S_LPROC32 && sym_record.kind() != S_GPROC32)
+ return std::nullopt;
+
+ ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
+ cantFail(SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc));
+
+ return FindMangledSymbol(SegmentOffset(proc.Segment, proc.CodeOffset),
+ proc.FunctionType);
+}
+
+std::optional<llvm::StringRef>
+SymbolFileNativePDB::FindMangledSymbol(SegmentOffset so,
+ TypeIndex function_type) {
+ auto symbol = m_index->publics().findByAddress(m_index->symrecords(),
+ so.segment, so.offset);
+ if (!symbol)
+ return std::nullopt;
+
+ llvm::StringRef name = symbol->first.Name;
+ // For functions, we might need to strip the mangled name. See
+ // StripMangledFunctionName for more info.
+ if (!function_type.isNoneType() &&
+ (symbol->first.Flags & PublicSymFlags::Function) != PublicSymFlags::None)
+ name = StripMangledFunctionName(name, function_type);
+
+ return name;
+}
+
+llvm::StringRef
+SymbolFileNativePDB::StripMangledFunctionName(const llvm::StringRef mangled,
+ PdbTypeSymId func_ty) {
+ // "In non-64 bit environments" (on x86 in pactice), __cdecl functions get
+ // prefixed with an underscore. For compilers using LLVM, this happens in LLVM
+ // (as opposed to the compiler frontend). Because of this, DWARF doesn't
+ // contain the "full" mangled name in DW_AT_linkage_name for these functions.
+ // We strip the mangling here for compatibility with DWARF. See
+ // llvm.org/pr161676 and
+ // https://learn.microsoft.com/en-us/cpp/build/reference/decorated-names#FormatC
+
+ if (!mangled.starts_with('_') ||
+ m_index->dbi().getMachineType() != PDB_Machine::x86)
+ return mangled;
+
+ CVType cvt = m_index->tpi().getType(func_ty.index);
+ PDB_CallingConv cc = PDB_CallingConv::NearC;
+ if (cvt.kind() == LF_PROCEDURE) {
+ ProcedureRecord proc;
+ if (llvm::Error error =
+ TypeDeserializer::deserializeAs<ProcedureRecord>(cvt, proc))
+ llvm::consumeError(std::move(error));
+ cc = proc.CallConv;
+ } else if (cvt.kind() == LF_MFUNCTION) {
+ MemberFunctionRecord mfunc;
+ if (llvm::Error error =
+ TypeDeserializer::deserializeAs<MemberFunctionRecord>(cvt, mfunc))
+ llvm::consumeError(std::move(error));
+ cc = mfunc.CallConv;
+ } else {
+ LLDB_LOG(GetLog(LLDBLog::Symbols), "Unexpected function type, got {0}",
+ cvt.kind());
+ return mangled;
+ }
+
+ if (cc == PDB_CallingConv::NearC || cc == PDB_CallingConv::FarC)
+ return mangled.drop_front();
+
+ return mangled;
+}
+
void SymbolFileNativePDB::CacheUdtDeclarations() {
for (CVType cvt : m_index->ipi().typeArray()) {
switch (cvt.kind()) {
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 2405f8b29933..a5fef354af65 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -140,6 +140,12 @@ public:
std::optional<PdbCompilandSymId> FindSymbolScope(PdbCompilandSymId id);
+ /// Find the mangled name for a function
+ ///
+ /// \param id A symbol ID of a S_LPROC32/S_GPROC32 record
+ /// \returns The mangled name of the function (if available)
+ std::optional<llvm::StringRef> FindMangledFunctionName(PdbCompilandSymId id);
+
void FindTypes(const lldb_private::TypeQuery &match,
lldb_private::TypeResults &results) override;
@@ -269,6 +275,20 @@ private:
void CacheUdtDeclarations();
llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id);
+ /// Find a symbol name at a specific address (`so`).
+ ///
+ /// \param[in] so The segment and offset where the symbol is located.
+ /// \param[in] function_type If the symbol is expected to be a function, this
+ /// has to be the type of the function. It's used to strip the name of
+ /// __cdecl functions on x86.
+ /// \returns The mangled symbol name if found, otherwise `std::nullopt`.
+ std::optional<llvm::StringRef> FindMangledSymbol(
+ SegmentOffset so,
+ llvm::codeview::TypeIndex function_type = llvm::codeview::TypeIndex());
+
+ llvm::StringRef StripMangledFunctionName(llvm::StringRef mangled,
+ PdbTypeSymId func_ty);
+
llvm::BumpPtrAllocator m_allocator;
lldb::addr_t m_obj_load_address = 0;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 1948f51c3f2e..82dfe7e54071 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -849,8 +849,20 @@ lldb::BasicType TypeSystemClang::GetBasicTypeEnumeration(llvm::StringRef name) {
{"unsigned long long int", eBasicTypeUnsignedLongLong},
// "int128"
+ //
+ // The following two lines are here only
+ // for the sake of backward-compatibility.
+ // Neither "__int128_t", nor "__uint128_t" are basic-types.
+ // They are typedefs.
{"__int128_t", eBasicTypeInt128},
{"__uint128_t", eBasicTypeUnsignedInt128},
+ // In order to be consistent with:
+ // - gcc's C programming language extension related to 128-bit integers
+ // https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html
+ // - the "BuiltinType::getName" method in LLVM
+ // the following two lines must be present:
+ {"__int128", eBasicTypeInt128},
+ {"unsigned __int128", eBasicTypeUnsignedInt128},
// "bool"
{"bool", eBasicTypeBool},
@@ -960,6 +972,12 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize(
if (type_name == "long double" &&
QualTypeMatchesBitSize(bit_size, ast, ast.LongDoubleTy))
return GetType(ast.LongDoubleTy);
+ if (type_name == "__bf16" &&
+ QualTypeMatchesBitSize(bit_size, ast, ast.BFloat16Ty))
+ return GetType(ast.BFloat16Ty);
+ if (type_name == "_Float16" &&
+ QualTypeMatchesBitSize(bit_size, ast, ast.Float16Ty))
+ return GetType(ast.Float16Ty);
// As Rust currently uses `TypeSystemClang`, match `f128` here as well so it
// doesn't get misinterpreted as `long double` on targets where they are
// the same size but different formats.
@@ -1792,6 +1810,8 @@ 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) {
+ assert(record_decl != base_class->getType()->getAsCXXRecordDecl() &&
+ "Base can't inherit from itself.");
if (RecordHasFields(base_class->getType()->getAsCXXRecordDecl()))
return true;
}
@@ -2609,7 +2629,7 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) {
case clang::Type::Enum:
case clang::Type::Record:
return llvm::cast<clang::TagType>(qual_type)
- ->getOriginalDecl()
+ ->getDecl()
->getDefinitionOrSelf();
default:
break;
@@ -2859,8 +2879,7 @@ 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->getOriginalDecl()) {
+ if (const clang::RecordDecl *record_decl = record_type->getDecl()) {
return record_decl->isAnonymousStructOrUnion();
}
}
@@ -3101,7 +3120,7 @@ TypeSystemClang::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type,
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
if (const clang::RecordDecl *record_decl =
- record_type->getOriginalDecl()->getDefinition()) {
+ record_type->getDecl()->getDefinition()) {
// We are looking for a structure that contains only floating point
// types
clang::RecordDecl::field_iterator field_pos,
@@ -3281,7 +3300,7 @@ bool TypeSystemClang::IsEnumerationType(lldb::opaque_compiler_type_t type,
GetCanonicalQualType(type)->getCanonicalTypeInternal());
if (enum_type) {
- IsIntegerType(enum_type->getOriginalDecl()
+ IsIntegerType(enum_type->getDecl()
->getDefinitionOrSelf()
->getIntegerType()
.getAsOpaquePtr(),
@@ -3509,7 +3528,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) {
- if (clang::TagDecl *tag_decl = tag_type->getOriginalDecl()->getDefinition())
+ if (clang::TagDecl *tag_decl = tag_type->getDecl()->getDefinition())
return tag_decl->isCompleteDefinition();
return false;
} else {
@@ -3762,7 +3781,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->getOriginalDecl()->isEntityBeingDefined();
+ return tag_type->getDecl()->isEntityBeingDefined();
return false;
}
@@ -3968,7 +3987,7 @@ 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)
- ->getOriginalDecl()
+ ->getDecl()
->getDefinitionOrSelf()
->getIntegerType()
.getAsOpaquePtr());
@@ -4208,7 +4227,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->getOriginalDecl();
+ const clang::RecordDecl *record_decl = record_type->getDecl();
if (record_decl->isUnion())
return lldb::eTypeClassUnion;
else if (record_decl->isStruct())
@@ -4684,9 +4703,9 @@ CompilerType TypeSystemClang::CreateTypedef(
clang::TagDecl *tdecl = nullptr;
if (!qual_type.isNull()) {
if (const clang::RecordType *rt = qual_type->getAs<clang::RecordType>())
- tdecl = rt->getOriginalDecl();
+ tdecl = rt->getDecl();
if (const clang::EnumType *et = qual_type->getAs<clang::EnumType>())
- tdecl = et->getOriginalDecl();
+ tdecl = et->getDecl();
}
// Check whether this declaration is an anonymous struct, union, or enum,
@@ -5033,6 +5052,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type,
case clang::BuiltinType::VectorPair:
case clang::BuiltinType::VectorQuad:
case clang::BuiltinType::DMR1024:
+ case clang::BuiltinType::DMR2048:
break;
// ARM -- Scalable Vector Extension
@@ -5365,7 +5385,7 @@ TypeSystemClang::GetNumChildren(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->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
const clang::CXXRecordDecl *cxx_record_decl =
llvm::dyn_cast<clang::CXXRecordDecl>(record_decl);
@@ -5562,7 +5582,7 @@ void TypeSystemClang::ForEachEnumerator(
llvm::dyn_cast<clang::EnumType>(GetCanonicalQualType(type));
if (enum_type) {
const clang::EnumDecl *enum_decl =
- enum_type->getOriginalDecl()->getDefinitionOrSelf();
+ enum_type->getDecl()->getDefinitionOrSelf();
if (enum_decl) {
CompilerType integer_type = GetType(enum_decl->getIntegerType());
@@ -5594,7 +5614,7 @@ uint32_t TypeSystemClang::GetNumFields(lldb::opaque_compiler_type_t type) {
llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
clang::RecordDecl *record_decl =
- record_type->getOriginalDecl()->getDefinition();
+ record_type->getDecl()->getDefinition();
if (record_decl) {
count = std::distance(record_decl->field_begin(),
record_decl->field_end());
@@ -5709,7 +5729,7 @@ CompilerType TypeSystemClang::GetFieldAtIndex(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->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
uint32_t field_idx = 0;
clang::RecordDecl::field_iterator field, field_end;
for (field = record_decl->field_begin(),
@@ -5895,7 +5915,7 @@ CompilerType TypeSystemClang::GetDirectBaseClassAtIndex(
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getOriginalDecl());
+ ->getDecl());
if (base_class->isVirtual())
*bit_offset_ptr =
record_layout.getVBaseClassOffset(base_class_decl)
@@ -5990,7 +6010,7 @@ CompilerType TypeSystemClang::GetVirtualBaseClassAtIndex(
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getOriginalDecl());
+ ->getDecl());
*bit_offset_ptr =
record_layout.getVBaseClassOffset(base_class_decl)
.getQuantity() *
@@ -6021,7 +6041,7 @@ 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->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
clang::DeclarationName decl_name(&getASTContext().Idents.get(name));
for (NamedDecl *decl : record_decl->lookup(decl_name)) {
@@ -6250,7 +6270,7 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr());
const clang::RecordDecl *record_decl =
- record_type->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
const clang::ASTRecordLayout &record_layout =
getASTContext().getASTRecordLayout(record_decl);
uint32_t child_idx = 0;
@@ -6271,7 +6291,7 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
base_class_decl = llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->getAs<clang::RecordType>()
- ->getOriginalDecl())
+ ->getDecl())
->getDefinitionOrSelf();
if (!TypeSystemClang::RecordHasFields(base_class_decl))
continue;
@@ -6282,7 +6302,7 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex(
base_class_decl = llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->getAs<clang::RecordType>()
- ->getOriginalDecl())
+ ->getDecl())
->getDefinitionOrSelf();
if (base_class->isVirtual()) {
@@ -6745,7 +6765,7 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
const clang::RecordType *record_type =
llvm::cast<clang::RecordType>(qual_type.getTypePtr());
const clang::RecordDecl *record_decl =
- record_type->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
assert(record_decl);
uint32_t child_idx = 0;
@@ -6812,7 +6832,7 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName(
child_indexes.push_back(child_idx);
parent_record_decl = elem.Base->getType()
->castAs<clang::RecordType>()
- ->getOriginalDecl()
+ ->getDecl()
->getDefinitionOrSelf();
}
}
@@ -6948,7 +6968,7 @@ TypeSystemClang::GetIndexOfChildWithName(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->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
assert(record_decl);
uint32_t child_idx = 0;
@@ -6967,7 +6987,7 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type,
llvm::cast<clang::CXXRecordDecl>(
base_class->getType()
->castAs<clang::RecordType>()
- ->getOriginalDecl())
+ ->getDecl())
->getDefinitionOrSelf();
if (omit_empty_base_classes &&
!TypeSystemClang::RecordHasFields(base_class_decl))
@@ -7088,7 +7108,7 @@ TypeSystemClang::GetDirectNestedTypeWithName(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->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
clang::DeclarationName decl_name(&getASTContext().Idents.get(name));
for (NamedDecl *decl : record_decl->lookup(decl_name)) {
@@ -7114,7 +7134,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->getOriginalDecl());
+ cxx_record_decl->getDecl());
return false;
}
@@ -7317,7 +7337,7 @@ clang::EnumDecl *TypeSystemClang::GetAsEnumDecl(const CompilerType &type) {
const clang::EnumType *enutype =
llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type));
if (enutype)
- return enutype->getOriginalDecl()->getDefinitionOrSelf();
+ return enutype->getDecl()->getDefinitionOrSelf();
return nullptr;
}
@@ -7325,7 +7345,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->getOriginalDecl()->getDefinitionOrSelf();
+ return record_type->getDecl()->getDefinitionOrSelf();
return nullptr;
}
@@ -7407,7 +7427,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->getOriginalDecl()))
+ llvm::dyn_cast<clang::RecordDecl>(TagT->getDecl()))
if (!Rec->getDeclName()) {
Rec->setAnonymousStructOrUnion(true);
field->setImplicit();
@@ -7493,7 +7513,7 @@ void TypeSystemClang::BuildIndirectFields(const CompilerType &type) {
continue;
clang::RecordDecl *field_record_decl =
- field_record_type->getOriginalDecl()->getDefinition();
+ field_record_type->getDecl()->getDefinition();
if (!field_record_decl)
continue;
@@ -7635,8 +7655,7 @@ 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->getOriginalDecl()->getDefinitionOrSelf();
+ const EnumDecl *enum_decl = enum_type->getDecl()->getDefinitionOrSelf();
qt = enum_decl->getIntegerType();
}
// Bools are handled separately because the clang AST printer handles bools
@@ -8296,7 +8315,7 @@ bool TypeSystemClang::SetHasExternalStorage(lldb::opaque_compiler_type_t type,
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
if (enum_decl) {
enum_decl->setHasExternalLexicalStorage(has_extern);
enum_decl->setHasExternalVisibleStorage(has_extern);
@@ -8334,7 +8353,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->getOriginalDecl();
+ clang::TagDecl *tag_decl = tag_type->getDecl();
if (tag_decl) {
tag_decl->startDefinition();
return true;
@@ -8369,8 +8388,7 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition(
// the definition.
const clang::TagType *tag_type = qual_type->getAs<clang::TagType>();
if (tag_type) {
- clang::TagDecl *tag_decl =
- tag_type->getOriginalDecl()->getDefinitionOrSelf();
+ clang::TagDecl *tag_decl = tag_type->getDecl()->getDefinitionOrSelf();
if (auto *cxx_record_decl = llvm::dyn_cast<CXXRecordDecl>(tag_decl)) {
// If we have a move constructor declared but no copy constructor we
@@ -8405,8 +8423,7 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition(
if (!enutype)
return false;
- clang::EnumDecl *enum_decl =
- enutype->getOriginalDecl()->getDefinitionOrSelf();
+ clang::EnumDecl *enum_decl = enutype->getDecl()->getDefinitionOrSelf();
if (enum_decl->isCompleteDefinition())
return true;
@@ -8464,8 +8481,7 @@ clang::EnumConstantDecl *TypeSystemClang::AddEnumerationValueToEnumerationType(
clang::EnumConstantDecl *enumerator_decl =
clang::EnumConstantDecl::CreateDeserialized(getASTContext(),
GlobalDeclID());
- clang::EnumDecl *enum_decl =
- enutype->getOriginalDecl()->getDefinitionOrSelf();
+ clang::EnumDecl *enum_decl = enutype->getDecl()->getDefinitionOrSelf();
enumerator_decl->setDeclContext(enum_decl);
if (name && name[0])
enumerator_decl->setDeclName(&getASTContext().Idents.get(name));
@@ -8500,8 +8516,7 @@ CompilerType TypeSystemClang::GetEnumerationIntegerType(CompilerType type) {
if (!enum_type)
return CompilerType();
- return GetType(
- enum_type->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType());
+ return GetType(enum_type->getDecl()->getDefinitionOrSelf()->getIntegerType());
}
CompilerType
@@ -8609,8 +8624,7 @@ 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->getOriginalDecl()->getDefinitionOrSelf();
+ const clang::EnumDecl *enum_decl = enutype->getDecl()->getDefinitionOrSelf();
lldb::offset_t offset = byte_offset;
bool qual_type_is_signed = qual_type->isSignedIntegerOrEnumerationType();
const uint64_t enum_svalue =
@@ -8886,7 +8900,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->getOriginalDecl();
+ const clang::RecordDecl *record_decl = record_type->getDecl();
if (level == eDescriptionLevelVerbose)
record_decl->dump(llvm_ostrm);
else {
@@ -8898,7 +8912,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->getOriginalDecl()) {
+ if (clang::TagDecl *tag_decl = tag_type->getDecl()) {
if (level == eDescriptionLevelVerbose)
tag_decl->dump(llvm_ostrm);
else
@@ -8938,7 +8952,7 @@ void TypeSystemClang::DumpTypeName(const CompilerType &type) {
case clang::Type::Enum: {
clang::EnumDecl *enum_decl =
- llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl();
+ llvm::cast<clang::EnumType>(qual_type)->getDecl();
if (enum_decl) {
printf("enum %s", enum_decl->getName().str().c_str());
}
@@ -9804,7 +9818,7 @@ bool TypeSystemClang::IsForcefullyCompleted(lldb::opaque_compiler_type_t type) {
llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr());
if (record_type) {
const clang::RecordDecl *record_decl =
- record_type->getOriginalDecl()->getDefinitionOrSelf();
+ record_type->getDecl()->getDefinitionOrSelf();
if (std::optional<ClangASTMetadata> metadata = GetMetadata(record_decl))
return metadata->IsForcefullyCompleted();
}
diff --git a/lldb/source/Protocol/MCP/MCPError.cpp b/lldb/source/Protocol/MCP/MCPError.cpp
index e140d11e12cf..cfac055ba5f1 100644
--- a/lldb/source/Protocol/MCP/MCPError.cpp
+++ b/lldb/source/Protocol/MCP/MCPError.cpp
@@ -22,14 +22,7 @@ MCPError::MCPError(std::string message, int64_t error_code)
void MCPError::log(llvm::raw_ostream &OS) const { OS << m_message; }
std::error_code MCPError::convertToErrorCode() const {
- return llvm::inconvertibleErrorCode();
-}
-
-lldb_protocol::mcp::Error MCPError::toProtocolError() const {
- lldb_protocol::mcp::Error error;
- error.code = m_error_code;
- error.message = m_message;
- return error;
+ return std::error_code(m_error_code, std::generic_category());
}
UnsupportedURI::UnsupportedURI(std::string uri) : m_uri(uri) {}
diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp
index 19030a3a4e5d..71323adbac5f 100644
--- a/lldb/source/Protocol/MCP/Server.cpp
+++ b/lldb/source/Protocol/MCP/Server.cpp
@@ -12,6 +12,7 @@
#include "lldb/Host/HostInfo.h"
#include "lldb/Protocol/MCP/MCPError.h"
#include "lldb/Protocol/MCP/Protocol.h"
+#include "lldb/Protocol/MCP/Transport.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/JSON.h"
@@ -108,48 +109,9 @@ Expected<std::vector<ServerInfo>> ServerInfo::Load() {
return infos;
}
-Server::Server(std::string name, std::string version, MCPTransport &client,
- LogCallback log_callback, ClosedCallback closed_callback)
- : m_name(std::move(name)), m_version(std::move(version)), m_client(client),
- m_log_callback(std::move(log_callback)),
- m_closed_callback(std::move(closed_callback)) {
- 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;
- }
-}
+Server::Server(std::string name, std::string version, LogCallback log_callback)
+ : m_name(std::move(name)), m_version(std::move(version)),
+ m_log_callback(std::move(log_callback)) {}
void Server::AddTool(std::unique_ptr<Tool> tool) {
if (!tool)
@@ -164,48 +126,64 @@ void Server::AddResourceProvider(
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;
+MCPBinderUP Server::Bind(MCPTransport &transport) {
+ MCPBinderUP binder_up = std::make_unique<MCPBinder>(transport);
+ binder_up->Bind<InitializeResult, InitializeParams>(
+ "initialize", &Server::InitializeHandler, this);
+ binder_up->Bind<ListToolsResult, void>("tools/list",
+ &Server::ToolsListHandler, this);
+ binder_up->Bind<CallToolResult, CallToolParams>(
+ "tools/call", &Server::ToolsCallHandler, this);
+ binder_up->Bind<ListResourcesResult, void>(
+ "resources/list", &Server::ResourcesListHandler, this);
+ binder_up->Bind<ReadResourceResult, ReadResourceParams>(
+ "resources/read", &Server::ResourcesReadHandler, this);
+ binder_up->Bind<void>("notifications/initialized",
+ [this]() { Log("MCP initialization complete"); });
+ return binder_up;
+}
+
+llvm::Error Server::Accept(MainLoop &loop, MCPTransportUP transport) {
+ MCPBinderUP binder = Bind(*transport);
+ MCPTransport *transport_ptr = transport.get();
+ binder->OnDisconnect([this, transport_ptr]() {
+ assert(m_instances.find(transport_ptr) != m_instances.end() &&
+ "Client not found in m_instances");
+ m_instances.erase(transport_ptr);
+ });
+ binder->OnError([this](llvm::Error err) {
+ Logv("Transport error: {0}", llvm::toString(std::move(err)));
+ });
+
+ auto handle = transport->RegisterMessageHandler(loop, *binder);
+ if (!handle)
+ return handle.takeError();
+
+ m_instances[transport_ptr] =
+ Client{std::move(*handle), std::move(transport), std::move(binder)};
+ return llvm::Error::success();
+}
+
+Expected<InitializeResult>
+Server::InitializeHandler(const InitializeParams &request) {
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;
+ return result;
}
-llvm::Expected<Response> Server::ToolsListHandler(const Request &request) {
- Response response;
-
+llvm::Expected<ListToolsResult> Server::ToolsListHandler() {
ListToolsResult result;
for (const auto &tool : m_tools)
result.tools.emplace_back(tool.second->GetDefinition());
- response.result = std::move(result);
-
- return response;
+ return result;
}
-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::Expected<CallToolResult>
+Server::ToolsCallHandler(const CallToolParams &params) {
llvm::StringRef tool_name = params.name;
if (tool_name.empty())
return llvm::createStringError("no tool name");
@@ -222,113 +200,50 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) {
if (!text_result)
return text_result.takeError();
- response.result = toJSON(*text_result);
-
- return response;
+ return text_result;
}
-llvm::Expected<Response> Server::ResourcesListHandler(const Request &request) {
- Response response;
-
+llvm::Expected<ListResourcesResult> Server::ResourcesListHandler() {
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;
+ return result;
}
-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;
+Expected<ReadResourceResult>
+Server::ResourcesReadHandler(const ReadResourceParams &params) {
+ StringRef uri_str = params.uri;
if (uri_str.empty())
- return llvm::createStringError("no resource uri");
+ return createStringError("no resource uri");
for (std::unique_ptr<ResourceProvider> &resource_provider_up :
m_resource_providers) {
- llvm::Expected<ReadResourceResult> result =
+ Expected<ReadResourceResult> result =
resource_provider_up->ReadResource(uri_str);
if (result.errorIsA<UnsupportedURI>()) {
- llvm::consumeError(result.takeError());
+ consumeError(result.takeError());
continue;
}
if (!result)
return result.takeError();
- Response response;
- response.result = std::move(*result);
- return response;
+ return *result;
}
return make_error<MCPError>(
- llvm::formatv("no resource handler for uri: {0}", uri_str).str(),
+ formatv("no resource handler for uri: {0}", uri_str).str(),
MCPError::kResourceNotFound);
}
ServerCapabilities Server::GetCapabilities() {
lldb_protocol::mcp::ServerCapabilities capabilities;
capabilities.supportsToolsList = true;
+ capabilities.supportsResourcesList = true;
// FIXME: Support sending notifications when a debugger/target are
// added/removed.
- capabilities.supportsResourcesList = false;
+ capabilities.supportsResourcesSubscribe = false;
return capabilities;
}
-
-void Server::Log(llvm::StringRef message) {
- if (m_log_callback)
- m_log_callback(message);
-}
-
-void Server::Received(const Request &request) {
- auto SendResponse = [this](const Response &response) {
- if (llvm::Error error = m_client.Send(response))
- 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) {
- Log("unexpected MCP message: response");
-}
-
-void Server::Received(const Notification &notification) {
- Handle(notification);
-}
-
-void Server::OnError(llvm::Error error) {
- Log(llvm::toString(std::move(error)));
-}
-
-void Server::OnClosed() {
- Log("EOF");
- if (m_closed_callback)
- m_closed_callback();
-}
diff --git a/lldb/source/Symbol/Function.cpp b/lldb/source/Symbol/Function.cpp
index 6114eccd935e..2be1e389aa1d 100644
--- a/lldb/source/Symbol/Function.cpp
+++ b/lldb/source/Symbol/Function.cpp
@@ -275,7 +275,7 @@ Function::~Function() = default;
void Function::GetStartLineSourceInfo(SupportFileSP &source_file_sp,
uint32_t &line_no) {
line_no = 0;
- source_file_sp.reset();
+ source_file_sp = std::make_shared<SupportFile>();
if (m_comp_unit == nullptr)
return;
diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp
index 970f6c474e37..6080703998ff 100644
--- a/lldb/source/Symbol/Symtab.cpp
+++ b/lldb/source/Symbol/Symtab.cpp
@@ -289,7 +289,7 @@ void Symtab::InitNameIndexes() {
std::vector<Language *> languages;
Language::ForEach([&languages](Language *l) {
languages.push_back(l);
- return true;
+ return IterationAction::Continue;
});
auto &name_to_index = GetNameToSymbolIndexMap(lldb::eFunctionNameTypeNone);
diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp
index 484d9badde39..8268d4ae4bb2 100644
--- a/lldb/source/Target/Language.cpp
+++ b/lldb/source/Target/Language.cpp
@@ -111,9 +111,9 @@ Language *Language::FindPlugin(llvm::StringRef file_path) {
ForEach([&result, file_path](Language *language) {
if (language->IsSourceFile(file_path)) {
result = language;
- return false;
+ return IterationAction::Stop;
}
- return true;
+ return IterationAction::Continue;
});
return result;
}
@@ -128,7 +128,8 @@ Language *Language::FindPlugin(LanguageType language,
return result;
}
-void Language::ForEach(std::function<bool(Language *)> callback) {
+void Language::ForEach(
+ llvm::function_ref<IterationAction(Language *)> callback) {
// If we want to iterate over all languages, we first have to complete the
// LanguagesMap.
static llvm::once_flag g_initialize;
@@ -153,7 +154,7 @@ void Language::ForEach(std::function<bool(Language *)> callback) {
}
for (auto *lang : loaded_plugins) {
- if (!callback(lang))
+ if (callback(lang) == IterationAction::Stop)
break;
}
}
@@ -270,6 +271,10 @@ const char *Language::GetNameForLanguageType(LanguageType language) {
return language_names[eLanguageTypeUnknown].name;
}
+llvm::StringRef Language::GetDisplayNameForLanguageType(LanguageType language) {
+ return SourceLanguage(language).GetDescription();
+}
+
void Language::PrintSupportedLanguagesForExpressions(Stream &s,
llvm::StringRef prefix,
llvm::StringRef suffix) {
@@ -289,9 +294,9 @@ void Language::PrintAllLanguages(Stream &s, const char *prefix,
}
void Language::ForAllLanguages(
- std::function<bool(lldb::LanguageType)> callback) {
+ llvm::function_ref<IterationAction(lldb::LanguageType)> callback) {
for (uint32_t i = 1; i < num_languages; i++) {
- if (!callback(language_names[i].type))
+ if (callback(language_names[i].type) == IterationAction::Stop)
break;
}
}
@@ -416,7 +421,7 @@ std::set<lldb::LanguageType> Language::GetSupportedLanguages() {
std::set<lldb::LanguageType> supported_languages;
ForEach([&](Language *lang) {
supported_languages.emplace(lang->GetLanguageType());
- return true;
+ return IterationAction::Continue;
});
return supported_languages;
}
@@ -542,9 +547,26 @@ Language::Language() = default;
// Destructor
Language::~Language() = default;
+static std::optional<llvm::dwarf::SourceLanguage>
+ToDwarfSourceLanguage(lldb::LanguageType language_type) {
+ if (language_type <= lldb::eLanguageTypeLastStandardLanguage)
+ return static_cast<llvm::dwarf::SourceLanguage>(language_type);
+
+ switch (language_type) {
+ case eLanguageTypeMipsAssembler:
+ return llvm::dwarf::DW_LANG_Mips_Assembler;
+ default:
+ return std::nullopt;
+ }
+}
+
SourceLanguage::SourceLanguage(lldb::LanguageType language_type) {
- auto lname =
- llvm::dwarf::toDW_LNAME((llvm::dwarf::SourceLanguage)language_type);
+ std::optional<llvm::dwarf::SourceLanguage> dwarf_lang =
+ ToDwarfSourceLanguage(language_type);
+ if (!dwarf_lang)
+ return;
+
+ auto lname = llvm::dwarf::toDW_LNAME(*dwarf_lang);
if (!lname)
return;
name = lname->first;
@@ -559,11 +581,8 @@ lldb::LanguageType SourceLanguage::AsLanguageType() const {
}
llvm::StringRef SourceLanguage::GetDescription() const {
- LanguageType type = AsLanguageType();
- if (type)
- return Language::GetNameForLanguageType(type);
return llvm::dwarf::LanguageDescription(
- (llvm::dwarf::SourceLanguageName)name);
+ static_cast<llvm::dwarf::SourceLanguageName>(name), version);
}
bool SourceLanguage::IsC() const { return name == llvm::dwarf::DW_LNAME_C; }
diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp
index c6d15fc6be0a..252bee2b5d72 100644
--- a/lldb/source/Target/RegisterContextUnwind.cpp
+++ b/lldb/source/Target/RegisterContextUnwind.cpp
@@ -52,6 +52,14 @@ static ConstString GetSymbolOrFunctionName(const SymbolContext &sym_ctx) {
return ConstString();
}
+static bool CallFrameAddressIsValid(ABISP abi_sp, lldb::addr_t cfa) {
+ if (cfa == LLDB_INVALID_ADDRESS)
+ return false;
+ if (abi_sp)
+ return abi_sp->CallFrameAddressIsValid(cfa);
+ return cfa != 0 && cfa != 1;
+}
+
RegisterContextUnwind::RegisterContextUnwind(Thread &thread,
const SharedPtr &next_frame,
SymbolContext &sym_ctx,
@@ -448,7 +456,7 @@ void RegisterContextUnwind::InitializeNonZerothFrame() {
ReadFrameAddress(row_register_kind, row->GetAFAValue(), m_afa);
// A couple of sanity checks..
- if (m_cfa == LLDB_INVALID_ADDRESS || m_cfa == 0 || m_cfa == 1) {
+ if (!CallFrameAddressIsValid(abi_sp, m_cfa)) {
UnwindLogMsg("could not find a valid cfa address");
m_frame_type = eNotAValidFrame;
return;
@@ -1847,9 +1855,11 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() {
active_row->GetCFAValue().GetValueType() !=
UnwindPlan::Row::FAValue::unspecified) {
addr_t new_cfa;
+ ProcessSP process_sp = m_thread.GetProcess();
+ ABISP abi_sp = process_sp ? process_sp->GetABI() : nullptr;
if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
- active_row->GetCFAValue(), new_cfa) ||
- new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ active_row->GetCFAValue(), new_cfa) ||
+ !CallFrameAddressIsValid(abi_sp, new_cfa)) {
UnwindLogMsg("failed to get cfa with fallback unwindplan");
m_fallback_unwind_plan_sp.reset();
m_full_unwind_plan_sp = original_full_unwind_plan_sp;
@@ -1870,10 +1880,9 @@ bool RegisterContextUnwind::TryFallbackUnwindPlan() {
if (ReadRegisterValueFromRegisterLocation(regloc, reg_info,
reg_value)) {
new_caller_pc_value = reg_value.GetAsUInt64();
- if (ProcessSP process_sp = m_thread.GetProcess()) {
- if (ABISP abi_sp = process_sp->GetABI())
- new_caller_pc_value = abi_sp->FixCodeAddress(new_caller_pc_value);
- }
+ if (process_sp)
+ new_caller_pc_value =
+ process_sp->FixCodeAddress(new_caller_pc_value);
}
}
}
@@ -1932,9 +1941,11 @@ bool RegisterContextUnwind::ForceSwitchToFallbackUnwindPlan() {
active_row->GetCFAValue().GetValueType() !=
UnwindPlan::Row::FAValue::unspecified) {
addr_t new_cfa;
+ ProcessSP process_sp = m_thread.GetProcess();
+ ABISP abi_sp = process_sp ? process_sp->GetABI() : nullptr;
if (!ReadFrameAddress(m_fallback_unwind_plan_sp->GetRegisterKind(),
- active_row->GetCFAValue(), new_cfa) ||
- new_cfa == 0 || new_cfa == 1 || new_cfa == LLDB_INVALID_ADDRESS) {
+ active_row->GetCFAValue(), new_cfa) ||
+ !CallFrameAddressIsValid(abi_sp, new_cfa)) {
UnwindLogMsg("failed to get cfa with fallback unwindplan");
m_fallback_unwind_plan_sp.reset();
return false;
@@ -2055,8 +2066,7 @@ bool RegisterContextUnwind::ReadFrameAddress(
RegisterNumber cfa_reg(m_thread, row_register_kind,
fa.GetRegisterNumber());
if (ReadGPRValue(cfa_reg, cfa_reg_contents)) {
- if (cfa_reg_contents == LLDB_INVALID_ADDRESS || cfa_reg_contents == 0 ||
- cfa_reg_contents == 1) {
+ if (!CallFrameAddressIsValid(abi_sp, cfa_reg_contents)) {
UnwindLogMsg(
"Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64,
cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB),
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index 8ad8d507268e..f7311a8b2441 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -148,6 +148,11 @@ TargetStats::ToJSON(Target &target,
target_metrics_json.try_emplace("targetCreateTime",
m_create_time.get().count());
+ if (m_load_core_time.get().count() > 0) {
+ target_metrics_json.try_emplace("loadCoreTime",
+ m_load_core_time.get().count());
+ }
+
json::Array breakpoints_array;
double totalBreakpointResolveTime = 0.0;
// Report both the normal breakpoint list and the internal breakpoint list.
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index f47dae2b2465..7fa1fc5d71f1 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -157,7 +157,8 @@ public:
ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
StoppointCallbackContext context(event_ptr, exe_ctx, true);
bp_site_sp->BumpHitCounts();
- m_should_stop = bp_site_sp->ShouldStop(&context);
+ m_should_stop =
+ bp_site_sp->ShouldStop(&context, m_async_stopped_locs);
} else {
Log *log = GetLog(LLDBLog::Process);
@@ -180,6 +181,7 @@ public:
}
const char *GetDescription() override {
+ // FIXME: only print m_async_stopped_locs.
if (m_description.empty()) {
ThreadSP thread_sp(m_thread_wp.lock());
if (thread_sp) {
@@ -202,7 +204,7 @@ public:
}
strm.Printf("breakpoint ");
- bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
+ m_async_stopped_locs.GetDescription(&strm, eDescriptionLevelBrief);
m_description = std::string(strm.GetString());
} else {
StreamString strm;
@@ -244,6 +246,12 @@ public:
}
uint32_t GetStopReasonDataCount() const override {
+ size_t num_async_locs = m_async_stopped_locs.GetSize();
+ // If we have async locations, they are the ones we should report:
+ if (num_async_locs > 0)
+ return num_async_locs * 2;
+
+ // Otherwise report the number of locations at this breakpoint's site.
lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
if (bp_site_sp)
return bp_site_sp->GetNumberOfConstituents() * 2;
@@ -251,22 +259,25 @@ public:
}
uint64_t GetStopReasonDataAtIndex(uint32_t idx) override {
- lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
- if (bp_site_sp) {
- uint32_t bp_index = idx / 2;
- BreakpointLocationSP bp_loc_sp(
- bp_site_sp->GetConstituentAtIndex(bp_index));
- if (bp_loc_sp) {
- if (idx & 1) {
- // FIXME: This might be a Facade breakpoint, so we need to fetch
- // the one that the thread actually hit, not the native loc ID.
-
- // Odd idx, return the breakpoint location ID
- return bp_loc_sp->GetID();
- } else {
- // Even idx, return the breakpoint ID
- return bp_loc_sp->GetBreakpoint().GetID();
- }
+ uint32_t bp_index = idx / 2;
+ BreakpointLocationSP loc_to_report_sp;
+
+ size_t num_async_locs = m_async_stopped_locs.GetSize();
+ if (num_async_locs > 0) {
+ // GetByIndex returns an empty SP if we ask past its contents:
+ loc_to_report_sp = m_async_stopped_locs.GetByIndex(bp_index);
+ } else {
+ lldb::BreakpointSiteSP bp_site_sp = GetBreakpointSiteSP();
+ if (bp_site_sp)
+ loc_to_report_sp = bp_site_sp->GetConstituentAtIndex(bp_index);
+ }
+ if (loc_to_report_sp) {
+ if (idx & 1) {
+ // Odd idx, return the breakpoint location ID
+ return loc_to_report_sp->GetID();
+ } else {
+ // Even idx, return the breakpoint ID
+ return loc_to_report_sp->GetBreakpoint().GetID();
}
}
return LLDB_INVALID_BREAK_ID;
@@ -335,8 +346,7 @@ protected:
// local list. That way if one of the breakpoint actions changes the
// site, then we won't be operating on a bad list.
BreakpointLocationCollection site_locations;
- size_t num_constituents =
- bp_site_sp->CopyConstituentsList(site_locations);
+ size_t num_constituents = m_async_stopped_locs.GetSize();
if (num_constituents == 0) {
m_should_stop = true;
@@ -436,16 +446,26 @@ protected:
// I'm just sticking the BreakpointSP's in a vector since I'm only
// using it to locally increment their retain counts.
+ // We are holding onto the breakpoint locations that were hit
+ // by this stop info between the "synchonous" ShouldStop and now.
+ // But an intervening action might have deleted one of the breakpoints
+ // we hit before we get here. So at the same time let's build a list
+ // of the still valid locations:
std::vector<lldb::BreakpointSP> location_constituents;
+ BreakpointLocationCollection valid_locs;
for (size_t j = 0; j < num_constituents; j++) {
- BreakpointLocationSP loc(site_locations.GetByIndex(j));
- location_constituents.push_back(
- loc->GetBreakpoint().shared_from_this());
+ BreakpointLocationSP loc_sp(m_async_stopped_locs.GetByIndex(j));
+ if (loc_sp->IsValid()) {
+ location_constituents.push_back(
+ loc_sp->GetBreakpoint().shared_from_this());
+ valid_locs.Add(loc_sp);
+ }
}
- for (size_t j = 0; j < num_constituents; j++) {
- lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
+ size_t num_valid_locs = valid_locs.GetSize();
+ for (size_t j = 0; j < num_valid_locs; j++) {
+ lldb::BreakpointLocationSP bp_loc_sp = valid_locs.GetByIndex(j);
StreamString loc_desc;
if (log) {
bp_loc_sp->GetDescription(&loc_desc, eDescriptionLevelBrief);
@@ -679,6 +699,7 @@ private:
lldb::break_id_t m_break_id;
bool m_was_all_internal;
bool m_was_one_shot;
+ BreakpointLocationCollection m_async_stopped_locs;
};
// StopInfoWatchpoint
diff --git a/lldb/source/Target/StructuredDataPlugin.cpp b/lldb/source/Target/StructuredDataPlugin.cpp
index 8e3ceb094b36..8ce7f9f391d7 100644
--- a/lldb/source/Target/StructuredDataPlugin.cpp
+++ b/lldb/source/Target/StructuredDataPlugin.cpp
@@ -52,7 +52,7 @@ void StructuredDataPlugin::InitializeBasePluginForDebugger(Debugger &debugger) {
if (!parent_command)
return;
- // Create the structured-data ommand object.
+ // Create the structured-data command object.
auto command_name = "structured-data";
auto command_sp = CommandObjectSP(new CommandStructuredData(interpreter));
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index fa98c2460649..e224a12e3346 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -139,6 +139,8 @@ private:
};
} // namespace
+static std::atomic<lldb::user_id_t> g_target_unique_id{1};
+
template <typename Installer>
static Status installExecutable(const Installer &installer) {
if (!installer.m_local_file || !installer.m_remote_file)
@@ -183,6 +185,7 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0),
m_latest_stop_hook_id(0), m_valid(true), m_suppress_stop_hooks(false),
m_is_dummy_target(is_dummy_target),
+ m_target_unique_id(g_target_unique_id++),
m_frame_recognizer_manager_up(
std::make_unique<StackFrameRecognizerManager>()) {
SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed");
@@ -2564,9 +2567,9 @@ ModuleSP Target::GetOrCreateModule(const ModuleSpec &orig_module_spec,
m_images.Append(module_sp, notify);
for (ModuleSP &old_module_sp : replaced_modules) {
- Module *old_module_ptr = old_module_sp.get();
+ auto old_module_wp = old_module_sp->weak_from_this();
old_module_sp.reset();
- ModuleList::RemoveSharedModuleIfOrphaned(old_module_ptr);
+ ModuleList::RemoveSharedModuleIfOrphaned(old_module_wp);
}
} else
module_sp.reset();
diff --git a/lldb/source/Target/TargetList.cpp b/lldb/source/Target/TargetList.cpp
index 7037dc2bea3c..188c2508a71e 100644
--- a/lldb/source/Target/TargetList.cpp
+++ b/lldb/source/Target/TargetList.cpp
@@ -428,6 +428,18 @@ TargetSP TargetList::FindTargetWithProcess(Process *process) const {
return target_sp;
}
+TargetSP TargetList::FindTargetByGloballyUniqueID(lldb::user_id_t id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
+ auto it = llvm::find_if(m_target_list, [id](const TargetSP &item) {
+ return item->GetGloballyUniqueID() == id;
+ });
+
+ if (it != m_target_list.end())
+ return *it;
+
+ return TargetSP();
+}
+
TargetSP TargetList::GetTargetSP(Target *target) const {
TargetSP target_sp;
if (!target)
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp
index 0628451a5abf..d49a01bdbcef 100644
--- a/lldb/source/Target/ThreadPlanStepOut.cpp
+++ b/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -397,9 +397,10 @@ bool ThreadPlanStepOut::ShouldStop(Event *event_ptr) {
else
return m_step_through_inline_plan_sp->ShouldStop(event_ptr);
} else if (m_step_out_further_plan_sp) {
- if (m_step_out_further_plan_sp->MischiefManaged())
+ if (m_step_out_further_plan_sp->MischiefManaged()) {
m_step_out_further_plan_sp.reset();
- else
+ done = true;
+ } else
return m_step_out_further_plan_sp->ShouldStop(event_ptr);
}
diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp
index 2040791882fd..89e05de97583 100644
--- a/lldb/source/Utility/XcodeSDK.cpp
+++ b/lldb/source/Utility/XcodeSDK.cpp
@@ -38,8 +38,8 @@ static llvm::StringRef GetName(XcodeSDK::Type type) {
return "XRSimulator";
case XcodeSDK::XROS:
return "XROS";
- case XcodeSDK::bridgeOS:
- return "bridgeOS";
+ case XcodeSDK::BridgeOS:
+ return "BridgeOS";
case XcodeSDK::Linux:
return "Linux";
case XcodeSDK::unknown:
@@ -83,8 +83,8 @@ static XcodeSDK::Type ParseSDKName(llvm::StringRef &name) {
return XcodeSDK::XRSimulator;
if (name.consume_front("XROS"))
return XcodeSDK::XROS;
- if (name.consume_front("bridgeOS"))
- return XcodeSDK::bridgeOS;
+ if (name.consume_front("BridgeOS"))
+ return XcodeSDK::BridgeOS;
if (name.consume_front("Linux"))
return XcodeSDK::Linux;
static_assert(XcodeSDK::Linux == XcodeSDK::numSDKTypes - 1,
@@ -204,7 +204,7 @@ std::string XcodeSDK::GetCanonicalName(XcodeSDK::Info info) {
case XROS:
name = "xros";
break;
- case bridgeOS:
+ case BridgeOS:
name = "bridgeos";
break;
case Linux:
diff --git a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
index 0cc505aedc4b..759b620105c4 100644
--- a/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
+++ b/lldb/test/API/commands/expression/diagnostics/TestExprDiagnostics.py
@@ -215,8 +215,20 @@ note: candidate function not viable: requires single argument 'x', but 2 argumen
details = diags.GetValueForKey("details")
- # Detail 1/2: undeclared 'a'
+ # Detail 1/3: note: requested expression language
diag = details.GetItemAtIndex(0)
+ self.assertEqual(str(diag.GetValueForKey("severity")), "note")
+ self.assertIn("Ran expression as 'C++", str(diag.GetValueForKey("message")))
+ self.assertIn(
+ "Ran expression as 'C++", str(diag.GetValueForKey("rendered"))
+ )
+ self.assertEqual(str(diag.GetValueForKey("source_location")), "")
+ self.assertEqual(str(diag.GetValueForKey("file")), "")
+ self.assertFalse(diag.GetValueForKey("hidden").GetBooleanValue())
+ self.assertFalse(diag.GetValueForKey("in_user_input").GetBooleanValue())
+
+ # Detail 2/3: undeclared 'a'
+ diag = details.GetItemAtIndex(1)
severity = diag.GetValueForKey("severity")
message = diag.GetValueForKey("message")
@@ -234,8 +246,8 @@ note: candidate function not viable: requires single argument 'x', but 2 argumen
self.assertFalse(hidden.GetBooleanValue())
self.assertTrue(in_user_input.GetBooleanValue())
- # Detail 1/2: undeclared 'b'
- diag = details.GetItemAtIndex(1)
+ # Detail 3/3: undeclared 'b'
+ diag = details.GetItemAtIndex(2)
message = diag.GetValueForKey("message")
self.assertIn("undeclared identifier 'b'", str(message))
diff --git a/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
index ed028a1a4ea3..4aea8009058b 100644
--- a/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/array/TestArrayFromStdModule.py
@@ -11,6 +11,9 @@ class TestCase(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
index 0fb6e883597d..5e819b7f6e16 100644
--- a/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/deque-basic/TestDequeFromStdModule.py
@@ -11,6 +11,9 @@ class TestBasicDeque(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
index e631a8737637..e8676b21bced 100644
--- a/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/deque-dbg-info-content/TestDbgInfoContentDequeFromStdModule.py
@@ -12,6 +12,9 @@ class TestDbgInfoContentDeque(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "18.0"])
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
index a6ba0810e68e..9581155be204 100644
--- a/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/forward_list/TestForwardListFromStdModule.py
@@ -11,6 +11,9 @@ class TestBasicForwardList(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
index b26bd7dedda8..923551ca64c3 100644
--- a/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/list-dbg-info-content/TestDbgInfoContentListFromStdModule.py
@@ -13,6 +13,9 @@ class TestDbgInfoContentList(TestBase):
@skipIf(compiler=no_match("clang"))
@skipIf(compiler="clang", compiler_version=["<", "12.0"])
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
index 6253a35e926d..d6c88926e3e4 100644
--- a/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/list/TestListFromStdModule.py
@@ -11,6 +11,9 @@ class TestBasicList(TestBase):
@add_test_categories(["libc++"])
@skipIf(compiler=no_match("clang"))
@skipIf(macos_version=["<", "15.0"])
+ @skipIf(
+ bugnumber="ASTImport of lambdas not supported: https://github.com/llvm/llvm-project/issues/149477"
+ )
def test(self):
self.build()
diff --git a/lldb/test/API/commands/protocol/TestMCPUnixSocket.py b/lldb/test/API/commands/protocol/TestMCPUnixSocket.py
index ea9255cc60ef..9edb97eb791b 100644
--- a/lldb/test/API/commands/protocol/TestMCPUnixSocket.py
+++ b/lldb/test/API/commands/protocol/TestMCPUnixSocket.py
@@ -32,3 +32,16 @@ class MCPUnixSocketCommandTestCase(TestBase):
startstr="MCP server started with connection listeners:",
substrs=[f"unix-connect://{socket_file}"],
)
+
+ self.expect(
+ "protocol-server get MCP",
+ startstr="MCP server connection listeners:",
+ substrs=[f"unix-connect://{socket_file}"],
+ )
+
+ self.runCmd("protocol-server stop MCP", check=False)
+ self.expect(
+ "protocol-server get MCP",
+ error=True,
+ substrs=["MCP server is not running"],
+ )
diff --git a/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py b/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py
index 2412b295bfb5..305e3cc397cf 100644
--- a/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py
+++ b/lldb/test/API/driver/quit_speed/TestQuitWithProcess.py
@@ -33,3 +33,28 @@ class DriverQuitSpeedTest(PExpectTest):
child.sendline("quit")
print("sent quit")
child.expect(pexpect.EOF, timeout=15)
+
+ @skipIfAsan
+ def test_run_quit_with_prompt(self):
+ """Test that the lldb driver's batch mode works correctly with trailing space in confimation."""
+ import pexpect
+
+ self.build()
+
+ exe = self.getBuildArtifact("a.out")
+
+ self.launch(executable=exe)
+ child = self.child
+
+ # Launch the process without a TTY so we don't have to interrupt:
+ child.sendline("process launch -n")
+ print("launched process")
+ child.expect(r"Process ([\d]*) launched:")
+ print("Got launch message")
+ child.sendline("quit")
+ print("sent quit")
+
+ child.expect(r".*LLDB will kill one or more processes.*")
+ # add trailing space to the confirmation.
+ child.sendline("yEs ")
+ child.expect(pexpect.EOF, timeout=15)
diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py
index 85c734012761..95868486b8cb 100644
--- a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py
+++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/resolver.py
@@ -51,7 +51,6 @@ class Resolver:
def get_short_help(self):
return "I am a python breakpoint resolver"
-
class ResolverModuleDepth(Resolver):
def __get_depth__(self):
return lldb.eSearchDepthModule
diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile
new file mode 100644
index 000000000000..695335e068c0
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/Makefile
@@ -0,0 +1,4 @@
+C_SOURCES := main.c
+CFLAGS_EXTRAS := -std=c99
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py
new file mode 100644
index 000000000000..2e176239facf
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/TestWasHit.py
@@ -0,0 +1,102 @@
+"""
+Test the WasHit feature of scripted breakpoints
+"""
+
+import os
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestWasHit(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24528")
+ def test_was_hit_resolver(self):
+ """Use facade breakpoints to emulate hitting some locations"""
+ self.build()
+ self.do_test()
+
+ def make_target_and_import(self):
+ target = lldbutil.run_to_breakpoint_make_target(self)
+ self.import_resolver_script()
+ return target
+
+ def import_resolver_script(self):
+ interp = self.dbg.GetCommandInterpreter()
+ error = lldb.SBError()
+
+ script_name = os.path.join(self.getSourceDir(), "bkpt_resolver.py")
+
+ command = "command script import " + script_name
+ self.runCmd(command)
+
+ def make_extra_args(self, sym_name, num_locs, loc_to_miss):
+ return f" -k symbol -v {sym_name} -k num_locs -v {num_locs} -k loc_to_miss -v {loc_to_miss} "
+
+ def do_test(self):
+ """This reads in a python file and sets a breakpoint using it."""
+
+ target = self.make_target_and_import()
+ extra_args = self.make_extra_args("stop_symbol", 4, 2)
+
+ bkpt_no = lldbutil.run_break_set_by_script(
+ self, "bkpt_resolver.FacadeExample", extra_args, 4
+ )
+
+ # Make sure the help text shows up in the "break list" output:
+ self.expect(
+ "break list",
+ substrs=["I am a facade resolver - sym: stop_symbol - num_locs: 4"],
+ msg="Help is listed in break list",
+ )
+
+ bkpt = target.FindBreakpointByID(bkpt_no)
+ self.assertTrue(bkpt.IsValid(), "Found the right breakpoint")
+
+ # Now continue. We should hit locations 1, 3 and 4:
+ (target, process, thread, bkpt) = lldbutil.run_to_breakpoint_do_run(
+ self, target, bkpt
+ )
+ # This location should be bkpt_no.1:
+ self.assertEqual(
+ thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint"
+ )
+ self.assertEqual(thread.stop_reason_data[1], 1, "First location hit is 1")
+
+ for loc in [3, 4]:
+ process.Continue()
+ self.assertEqual(
+ thread.stop_reason, lldb.eStopReasonBreakpoint, "Hit breakpoint"
+ )
+ self.assertEqual(
+ thread.stop_reason_data[0], bkpt_no, "Hit the right breakpoint"
+ )
+ self.assertEqual(
+ thread.stop_reason_data[1], loc, f"Hit the right location: {loc}"
+ )
+
+ # At this point we should have hit three of the four locations, and not location 1.2.
+ # Check that that is true, and that the descriptions for the location are the ones
+ # the resolver provided.
+ self.assertEqual(bkpt.hit_count, 3, "Hit three locations")
+ for loc_id in range(1, 4):
+ bkpt_loc = bkpt.FindLocationByID(loc_id)
+ self.assertTrue(bkpt_loc.IsValid(), f"{loc_id} was invalid.")
+ if loc_id != 2:
+ self.assertEqual(
+ bkpt_loc.hit_count, 1, f"Loc {loc_id} hit count was wrong"
+ )
+ else:
+ self.assertEqual(bkpt_loc.hit_count, 0, "We didn't skip loc 2")
+ stream = lldb.SBStream()
+ self.assertTrue(
+ bkpt_loc.GetDescription(stream, lldb.eDescriptionLevelFull),
+ f"Didn't get description for {loc_id}",
+ )
+ self.assertIn(
+ f"Location index: {loc_id}",
+ stream.GetData(),
+ f"Wrong desciption for {loc_id}",
+ )
diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py
new file mode 100644
index 000000000000..acc8513e3e3e
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/bkpt_resolver.py
@@ -0,0 +1,49 @@
+import lldb
+
+
+class FacadeExample:
+ def __init__(self, bkpt, extra_args, dict):
+ self.bkpt = bkpt
+ self.extra_args = extra_args
+ self.base_sym = None
+ self.facade_locs = []
+ self.facade_locs_desc = []
+ self.cur_facade_loc = 1
+
+ self.sym_name = extra_args.GetValueForKey("symbol").GetStringValue(100)
+ self.num_locs = extra_args.GetValueForKey("num_locs").GetIntegerValue(5)
+ self.loc_to_miss = extra_args.GetValueForKey("loc_to_miss").GetIntegerValue(
+ 10000
+ )
+
+ def __callback__(self, sym_ctx):
+ self.base_sym = sym_ctx.module.FindSymbol(self.sym_name, lldb.eSymbolTypeCode)
+ if self.base_sym.IsValid():
+ self.bkpt.AddLocation(self.base_sym.GetStartAddress())
+ # Locations are 1 based, so to keep things simple, I'm making
+ # the array holding locations 1 based as well:
+ self.facade_locs_desc.append(
+ "This is the zero index, you shouldn't see this"
+ )
+ self.facade_locs.append(None)
+ for i in range(1, self.num_locs + 1):
+ self.facade_locs_desc.append(f"Location index: {i}")
+ self.facade_locs.append(self.bkpt.AddFacadeLocation())
+
+ def get_short_help(self):
+ return f"I am a facade resolver - sym: {self.sym_name} - num_locs: {self.num_locs} - locs_to_miss: {self.loc_to_miss}"
+
+ def was_hit(self, frame, bp_loc):
+ tmp_loc = self.cur_facade_loc
+
+ self.cur_facade_loc = self.cur_facade_loc + 1
+ if self.cur_facade_loc == self.num_locs + 1:
+ self.cur_facade_loc = 1
+
+ if tmp_loc == self.loc_to_miss:
+ return None
+
+ return self.facade_locs[tmp_loc]
+
+ def get_location_description(self, bp_loc, desc_level):
+ return self.facade_locs_desc[bp_loc.id]
diff --git a/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c
new file mode 100644
index 000000000000..b8f977e49351
--- /dev/null
+++ b/lldb/test/API/functionalities/breakpoint/scripted_bkpt/was_hit/main.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+
+int stop_symbol() {
+ static int s_cnt = 0;
+ printf("I am in the stop symbol: %d\n", s_cnt++);
+ return s_cnt;
+}
+
+int main() {
+ for (int i = 0; i < 100; i++) {
+ stop_symbol();
+ }
+ return 0;
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/TestDataFormatterInvalidAtomic.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/TestDataFormatterInvalidAtomic.py
new file mode 100644
index 000000000000..76b8e7b40f44
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/TestDataFormatterInvalidAtomic.py
@@ -0,0 +1,45 @@
+"""
+Test formatting of `std::atomic`s not from any STL
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class InvalidAtomicDataFormatterTestCase(TestBase):
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "Set break point at this line.", lldb.SBFileSpec("main.cpp")
+ )
+
+ self.expect_expr(
+ "a",
+ result_children=[
+ ValueCheck(name="foo", value="1"),
+ ValueCheck(name="bar", value="2"),
+ ],
+ )
+ self.expect_expr(
+ "b",
+ result_children=[
+ ValueCheck(name="foo", value="3"),
+ ValueCheck(name="bar", value="4"),
+ ],
+ )
+
+ self.expect_expr(
+ "c",
+ result_children=[
+ ValueCheck(name="foo", value="5"),
+ ValueCheck(name="bar", value="6"),
+ ],
+ )
+ self.expect_expr(
+ "d",
+ result_children=[
+ ValueCheck(name="foo", value="7"),
+ ValueCheck(name="bar", value="8"),
+ ],
+ )
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/main.cpp
new file mode 100644
index 000000000000..7b4c51c921a9
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic-simulators/invalid-atomic/main.cpp
@@ -0,0 +1,23 @@
+namespace std {
+template <typename T> struct atomic {
+ int foo;
+ int bar;
+};
+
+namespace __1 {
+template <typename T> struct atomic {
+ int foo;
+ int bar;
+};
+} // namespace __1
+} // namespace std
+
+int main() {
+ std::atomic<int> a{1, 2};
+ std::atomic<void> b{3, 4};
+
+ std::__1::atomic<int> c{5, 6};
+ std::__1::atomic<void> d{7, 8};
+
+ return 0; // Set break point at this line.
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered_map-iterator/TestDataFormatterStdUnorderedMap.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered_map-iterator/TestDataFormatterStdUnorderedMap.py
index 1e920faab639..45f7b5be465c 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered_map-iterator/TestDataFormatterStdUnorderedMap.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered_map-iterator/TestDataFormatterStdUnorderedMap.py
@@ -124,11 +124,6 @@ class StdUnorderedMapDataFormatterTestCase(TestBase):
self.check_ptr_ptr("ptr5")
self.check_ptr_ptr("ptr6")
- @expectedFailureAll(
- bugnumber="https://github.com/llvm/llvm-project/issues/146040",
- compiler="clang",
- compiler_version=["<", "21"],
- )
@add_test_categories(["libc++"])
def test_ptr_libcxx(self):
self.build(dictionary={"USE_LIBCPP": 1})
diff --git a/lldb/test/API/functionalities/json/symbol-file/Makefile b/lldb/test/API/functionalities/json/symbol-file/Makefile
index 13bc164582ee..5d05d95fc842 100644
--- a/lldb/test/API/functionalities/json/symbol-file/Makefile
+++ b/lldb/test/API/functionalities/json/symbol-file/Makefile
@@ -1,4 +1,5 @@
C_SOURCES := main.c
+CFLAGS_EXTRAS := -no-pie
all: stripped.out
diff --git a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
index f06c9ae14bb7..d7249df350fc 100644
--- a/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
+++ b/lldb/test/API/functionalities/stats_api/TestStatisticsAPI.py
@@ -1,6 +1,7 @@
# Test the SBAPI for GetStatistics()
import json
+
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
@@ -54,6 +55,11 @@ class TestStatsAPI(TestBase):
stats_json,
'Make sure the "frameVariable" key in in target.GetStatistics()["targets"][0]',
)
+ self.assertNotIn(
+ "loadCoreTime",
+ stats_json,
+ "LoadCoreTime should not be present in a live, non-coredump target",
+ )
expressionEvaluation = stats_json["expressionEvaluation"]
self.assertIn(
"successes",
@@ -157,3 +163,25 @@ class TestStatsAPI(TestBase):
stats_force.GetAsJSON(stream_force)
debug_stats_force = json.loads(stream_force.GetData())
self.assertEqual(debug_stats_force["totalDebugInfoByteSize"], 445)
+
+ def test_core_load_time(self):
+ """
+ Test to see if the coredump path is included in statistics dump.
+ """
+ yaml_file = "arm64-minidump-build-ids.yaml"
+ src_dir = self.getSourceDir()
+ minidump_path = self.getBuildArtifact(os.path.basename(yaml_file) + ".dmp")
+ self.yaml2obj(os.path.join(src_dir, yaml_file), minidump_path)
+ target = self.dbg.CreateTarget(None)
+ process = target.LoadCore(minidump_path)
+ self.assertTrue(process.IsValid())
+
+ stats_options = lldb.SBStatisticsOptions()
+ stats = target.GetStatistics(stats_options)
+ stream = lldb.SBStream()
+ stats.GetAsJSON(stream)
+ debug_stats = json.loads(stream.GetData())
+ self.assertTrue("targets" in debug_stats)
+ target_info = debug_stats["targets"][0]
+ self.assertTrue("loadCoreTime" in target_info)
+ self.assertTrue(float(target_info["loadCoreTime"]) > 0.0)
diff --git a/lldb/test/API/functionalities/stats_api/arm64-minidump-build-ids.yaml b/lldb/test/API/functionalities/stats_api/arm64-minidump-build-ids.yaml
new file mode 100644
index 000000000000..4acbc409d808
--- /dev/null
+++ b/lldb/test/API/functionalities/stats_api/arm64-minidump-build-ids.yaml
@@ -0,0 +1,19 @@
+--- !minidump
+Streams:
+ - Type: SystemInfo
+ Processor Arch: ARM
+ Platform ID: Linux
+ CSD Version: '15E216'
+ CPU:
+ CPUID: 0x00000000
+ - Type: ModuleList
+ Modules:
+ - Base of Image: 0x0000000000001000
+ Size of Image: 0x00001000
+ Module Name: '/tmp/a'
+ CodeView Record: 4C4570420102030405060708090A0B0C0D0E0F1011121314
+ - Base of Image: 0x0000000000001000
+ Size of Image: 0x00001000
+ Module Name: '/tmp/b'
+ CodeView Record: 4C4570420A141E28323C46505A646E78828C96A0AAB4BEC8
+...
diff --git a/lldb/test/API/functionalities/thread/step_out_line0/Makefile b/lldb/test/API/functionalities/thread/step_out_line0/Makefile
new file mode 100644
index 000000000000..10495940055b
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_line0/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/thread/step_out_line0/TestThreadStepOutLine0.py b/lldb/test/API/functionalities/thread/step_out_line0/TestThreadStepOutLine0.py
new file mode 100644
index 000000000000..2707ca852f66
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_line0/TestThreadStepOutLine0.py
@@ -0,0 +1,35 @@
+"""
+Test stepping out of a function when the return location is an unsuitable
+stopping point.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class ThreadStepOutLine0TestCase(TestBase):
+ def test(self):
+ self.build()
+ target, process, thread, _ = lldbutil.run_to_source_breakpoint(
+ self, "// Set breakpoint here", lldb.SBFileSpec("main.c")
+ )
+ correct_stepped_out_line = line_number("main.c", "// Should stop here")
+ return_statement_line = line_number("main.c", "// Ran too far")
+ safety_bp = target.BreakpointCreateByLocation(
+ lldb.SBFileSpec("main.c"), return_statement_line
+ )
+ self.assertTrue(safety_bp.IsValid())
+
+ error = lldb.SBError()
+ thread.StepOut(error)
+ self.assertTrue(error.Success())
+
+ frame = thread.GetSelectedFrame()
+ self.assertEqual(
+ frame.line_entry.GetLine(),
+ correct_stepped_out_line,
+ "Step-out lost control of execution, ran too far",
+ )
diff --git a/lldb/test/API/functionalities/thread/step_out_line0/main.c b/lldb/test/API/functionalities/thread/step_out_line0/main.c
new file mode 100644
index 000000000000..ad0c1c05d695
--- /dev/null
+++ b/lldb/test/API/functionalities/thread/step_out_line0/main.c
@@ -0,0 +1,11 @@
+int step_out_of_here(int a) {
+ return a + 5; // Set breakpoint here
+}
+
+int main() {
+#line 0
+ int v = step_out_of_here(3) + 7;
+#line 9
+ v += 11; // Should stop here
+ return v; // Ran too far
+}
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py
index 768dd6fe6867..50ea17370524 100644
--- a/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/TestCortexMExceptionUnwind.py
@@ -12,21 +12,7 @@ from lldbsuite.test import lldbutil
class TestCortexMExceptionUnwind(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- # on the lldb-remote-linux-ubuntu CI, the binary.json's triple of
- # armv7m-apple is not being set in the Target triple, and we're
- # picking the wrong ABI plugin, ABISysV_arm.
- # ABISysV_arm::CreateDefaultUnwindPlan() doesn't have a way to detect
- # arm/thumb for a stack frame, or even the Target's triple for a
- # Cortex-M part that is always thumb. It hardcodes r11 as the frame
- # pointer register, which is correct for arm code but not thumb.
- # It is never correct # on a Cortex-M target.
- # The Darwin ABIMacOSX_arm diverges from AAPCS and always uses r7 for
- # the frame pointer -- the thumb convention -- whether executing arm or
- # thumb. So its CreateDefaultUnwindPlan picks the correct register for
- # the frame pointer, and we can walk the stack.
- # ABISysV_arm::CreateDefaultUnwindPlan will only get one frame and
- # not be able to continue.
- @skipIfRemote
+ @skipIfLLVMTargetMissing("ARM")
def test_no_fpu(self):
"""Test that we can backtrace correctly through an ARM Cortex-M Exception return stack"""
@@ -42,6 +28,9 @@ class TestCortexMExceptionUnwind(TestBase):
core = self.getBuildArtifact("core")
self.yaml2macho_core("armv7m-nofpu-exception.yaml", core, exe_uuid)
+ if self.TraceOn():
+ self.runCmd("log enable lldb unwind")
+
process = target.LoadCore(core)
self.assertTrue(process.IsValid())
@@ -55,14 +44,9 @@ class TestCortexMExceptionUnwind(TestBase):
thread = process.GetThreadAtIndex(0)
self.assertTrue(thread.IsValid())
- # We have 4 named stack frames and two unnamed
- # frames above that. The topmost two stack frames
- # were not interesting for this test, so I didn't
- # create symbols for them.
- self.assertEqual(thread.GetNumFrames(), 6)
+ self.assertEqual(thread.GetNumFrames(), 3)
stackframe_names = [
"exception_catcher",
- "exception_catcher",
"exception_thrower",
"main",
]
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml
index 9ce5ff49d9b6..0b4e1f8fac3e 100644
--- a/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/armv7m-nofpu-exception.yaml
@@ -2,8 +2,8 @@ cpu: armv7m
threads:
- regsets:
- flavor: gpr
- registers: [{name: sp, value: 0x2000fe70}, {name: r7, value: 0x2000fe80},
- {name: pc, value: 0x0020392c}, {name: lr, value: 0x0020392d}]
+ registers: [{name: sp, value: 0x2000fe88}, {name: r7, value: 0x2000fe88},
+ {name: pc, value: 0x00203916}, {name: lr, value: 0x0020392d}]
memory-regions:
# stack memory fetched via
# (lldb) p/x $sp
@@ -14,7 +14,7 @@ memory-regions:
0x0000002a, 0x20010e58, 0x00203923, 0x00000001,
0x2000fe88, 0x00203911, 0x2000ffdc, 0xfffffff9,
0x00000102, 0x00000002, 0x000003f0, 0x0000002a,
- 0x20012620, 0x00203215, 0x00203366, 0x81000200,
+ 0x20012620, 0x00203215, 0x00202a92, 0x81000200,
0x00203215, 0x200128b0, 0x0024928d, 0x2000fecc,
0x002491ed, 0x20010e58, 0x20010e4c, 0x2000ffa0,
0x200107a0, 0x0000003c, 0x200116e8, 0x200108b0,
@@ -62,3 +62,26 @@ memory-regions:
0x98, 0xae, 0x28, 0x00
]
+ # exception_thrower
+ # (lldb) disass -b -c 12 -n exception_thrower
+ # 0x202a88 <+0>: 0xb5f0 push {r4, r5, r6, r7, lr}
+ # 0x202a8a <+2>: 0xaf03 add r7, sp, #0xc
+ # 0x202a8c <+4>: 0xe92d0f00 push.w {r8, r9, r10, r11}
+ # 0x202a90 <+8>: 0xb0c3 sub sp, #0x10c
+ # 0x202a92 <+10>: 0xf7ffffd9 bl 0x202a48
+ - addr: 0x202a88
+ UInt8: [
+ 0xf0, 0xb5, 0x03, 0xaf, 0x2d, 0xe9, 0x00, 0x0f,
+ 0xc3, 0xb0, 0xff, 0xf7, 0xd9, 0xff, 0xff, 0xf7
+ ]
+
+ # main:
+ # 0x202a7e <+0>: push {r7, lr}
+ # 0x202a80 <+2>: mov r7, sp
+ # 0x202a82 <+4>: bl 0x202a88 ; exception_thrower
+ # 0x202a86 <+8>: nop
+ - addr: 0x202a7e
+ UInt8: [
+ 0x80, 0xb5, 0x6f, 0x46, 0x00, 0xf0, 0x01, 0xf8,
+ 0x00, 0xbf
+ ]
diff --git a/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json
index 8fcd5307ff82..0de0169f7adb 100644
--- a/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json
+++ b/lldb/test/API/functionalities/unwind/cortex-m-exception/binary.json
@@ -1,5 +1,5 @@
{
- "triple": "armv7m-apple",
+ "triple": "armv7m--",
"uuid": "2D157DBA-53C9-3AC7-B5A1-9D336EC831CB",
"type": "executable",
"sections": [
@@ -28,13 +28,13 @@
{
"name": "exception_catcher",
"type": "code",
- "size": 44,
+ "size": 32,
"address": 2111760
},
{
"name": "exception_thrower",
"type": "code",
- "size": 2652,
+ "size": 16,
"address": 2108040
}
]
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
index 87d8adb42b82..2d3e4f7cdd47 100644
--- a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
@@ -10,6 +10,11 @@ from lldbsuite.test import lldbutil
class AbiTagStructorsTestCase(TestBase):
+ @skipIf(
+ compiler="clang",
+ compiler_version=["<", "22"],
+ bugnumber="Required Clang flag not supported",
+ )
@expectedFailureAll(oslist=["windows"])
def test_with_structor_linkage_names(self):
self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
@@ -73,7 +78,16 @@ class AbiTagStructorsTestCase(TestBase):
Test that without linkage names on structor declarations we can't call
ABI-tagged structors.
"""
- self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+ # In older versions of Clang the -gno-structor-decl-linkage-names
+ # behaviour was the default.
+ if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
+ [">=", "22.0"]
+ ):
+ self.build(
+ dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"}
+ )
+ else:
+ self.build()
lldbutil.run_to_source_breakpoint(
self, "Break here", lldb.SBFileSpec("main.cpp", False)
@@ -105,12 +119,23 @@ class AbiTagStructorsTestCase(TestBase):
"expression TaggedLocal()", error=True, substrs=["Couldn't look up symbols"]
)
+ @skipIf(compiler="clang", compiler_version=["<", "22"])
@expectedFailureAll(oslist=["windows"])
- def test_nested_no_structor_linkage_names(self):
+ def test_nested_with_structor_linkage_names(self):
self.build(dictionary={"CXXFLAGS_EXTRAS": "-gstructor-decl-linkage-names"})
self.do_nested_structor_test()
@expectedFailureAll(oslist=["windows"])
- def test_nested_with_structor_linkage_names(self):
- self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+ def test_nested_no_structor_linkage_names(self):
+ # In older versions of Clang the -gno-structor-decl-linkage-names
+ # behaviour was the default.
+ if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
+ [">=", "22.0"]
+ ):
+ self.build(
+ dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"}
+ )
+ else:
+ self.build()
+
self.do_nested_structor_test()
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 c0545c70c84e..b3bed43c7587 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
@@ -6,6 +6,11 @@ from lldbsuite.test import lldbutil
class ExprDefinitionInDylibTestCase(TestBase):
+ @skipIf(
+ compiler="clang",
+ compiler_version=["<", "22"],
+ bugnumber="Required Clang flag not supported",
+ )
@skipIfWindows
def test_with_structor_linkage_names(self):
"""
@@ -74,7 +79,16 @@ class ExprDefinitionInDylibTestCase(TestBase):
Tests that if structor declarations don't have linkage names, we can't
call ABI-tagged constructors. But non-tagged ones are fine.
"""
- self.build(dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"})
+ # In older versions of Clang the -gno-structor-decl-linkage-names
+ # behaviour was the default.
+ if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
+ [">=", "22.0"]
+ ):
+ self.build(
+ dictionary={"CXXFLAGS_EXTRAS": "-gno-structor-decl-linkage-names"}
+ )
+ else:
+ self.build()
target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
self.assertTrue(target, VALID_TARGET)
@@ -95,6 +109,6 @@ class ExprDefinitionInDylibTestCase(TestBase):
self.expect_expr("Foo(10)", result_type="Foo")
- self.expect("Base()", error=True)
+ self.expect("expr Base()", error=True)
- self.expect("Bar()", error=True)
+ self.expect("expr Bar()", error=True)
diff --git a/lldb/test/API/lang/cpp/floating-types-specialization/Makefile b/lldb/test/API/lang/cpp/floating-types-specialization/Makefile
new file mode 100644
index 000000000000..99998b20bcb0
--- /dev/null
+++ b/lldb/test/API/lang/cpp/floating-types-specialization/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/floating-types-specialization/TestCppFloatingTypesSpecialization.py b/lldb/test/API/lang/cpp/floating-types-specialization/TestCppFloatingTypesSpecialization.py
new file mode 100644
index 000000000000..979391f3681f
--- /dev/null
+++ b/lldb/test/API/lang/cpp/floating-types-specialization/TestCppFloatingTypesSpecialization.py
@@ -0,0 +1,37 @@
+import lldb
+import lldbsuite.test.lldbplatformutil as lldbplatformutil
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+ @skipIf(compiler="clang", compiler_version=["<", "17.0"])
+ def test(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp", False)
+ )
+
+ # On 32-bit Arm, you have to have the bfloat16 extension, or an FPU while
+ # not using the soft float mode. The target we assume has none of that
+ # so instead of __bf16 we get __fp16.
+ is_arm_32_bit = lldbplatformutil.getArchitecture() == "arm"
+
+ self.expect_expr(
+ "f0", result_type=("Foo<__fp16>" if is_arm_32_bit else "Foo<__bf16>")
+ )
+
+ # When __bf16 is actually __fp16, f1 looks like it inherits from itself.
+ # Which clang allows but LLDB fails to evaluate.
+ if not is_arm_32_bit:
+ self.expect_expr("f1", result_type="Foo<__fp16>")
+
+ # Test sizeof to ensure while computing layout we don't do
+ # infinite recursion.
+ v = self.frame().EvaluateExpression("sizeof(f0)")
+ self.assertEqual(v.GetValueAsUnsigned() > 0, True)
+
+ if not is_arm_32_bit:
+ v = self.frame().EvaluateExpression("sizeof(f1)")
+ self.assertEqual(v.GetValueAsUnsigned() > 0, True)
diff --git a/lldb/test/API/lang/cpp/floating-types-specialization/main.cpp b/lldb/test/API/lang/cpp/floating-types-specialization/main.cpp
new file mode 100644
index 000000000000..e3e8a3767fef
--- /dev/null
+++ b/lldb/test/API/lang/cpp/floating-types-specialization/main.cpp
@@ -0,0 +1,11 @@
+template <typename T> struct Foo;
+
+template <> struct Foo<__bf16> {};
+
+template <> struct Foo<_Float16> : Foo<__bf16> {};
+
+int main() {
+ Foo<__bf16> f0;
+ Foo<_Float16> f1;
+ return 0; // break here
+}
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/Makefile b/lldb/test/API/lang/cpp/function-call-from-object-file/Makefile
new file mode 100644
index 000000000000..285bbfbbca4f
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp lib1.cpp lib2.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/TestFunctionCallFromObjectFile.py b/lldb/test/API/lang/cpp/function-call-from-object-file/TestFunctionCallFromObjectFile.py
new file mode 100644
index 000000000000..f0a7aef182a6
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/TestFunctionCallFromObjectFile.py
@@ -0,0 +1,29 @@
+"""
+Tests that we can call functions that have definitions in multiple
+CUs in the debug-info (which is the case for functions defined in headers).
+The linker will most likely de-duplicate the functiond definitions when linking
+the final executable. On Darwin, this will create a debug-map that LLDB will use
+to fix up object file addresses to addresses in the linked executable. However,
+if we parsed the DIE from the object file whose functiond definition got stripped
+by the linker, LLDB needs to ensure it can still resolve the function symbol it
+got for it.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestFunctionCallFromObjectFile(TestBase):
+ def test_lib1(self):
+ self.build()
+ lldbutil.run_to_name_breakpoint(self, "lib1_func")
+
+ self.expect_expr("Foo{}.foo()", result_type="int", result_value="15")
+
+ def test_lib2(self):
+ self.build()
+ lldbutil.run_to_name_breakpoint(self, "lib2_func")
+
+ self.expect_expr("Foo{}.foo()", result_type="int", result_value="15")
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/common.h b/lldb/test/API/lang/cpp/function-call-from-object-file/common.h
new file mode 100644
index 000000000000..76e23be6b97a
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/common.h
@@ -0,0 +1,8 @@
+#ifndef COMMON_H_IN
+#define COMMON_H_IN
+
+struct Foo {
+ int foo() { return 15; }
+};
+
+#endif // COMMON_H_IN
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/lib1.cpp b/lldb/test/API/lang/cpp/function-call-from-object-file/lib1.cpp
new file mode 100644
index 000000000000..b97bcc1b712b
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/lib1.cpp
@@ -0,0 +1,8 @@
+#include "common.h"
+
+// Parameter "Foo*" forces LLDB to parse "Foo" from the object
+// file that it is stopped in.
+void lib1_func(Foo *) {
+ // Force definition into lib1.o debug-info.
+ Foo{}.foo();
+}
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/lib2.cpp b/lldb/test/API/lang/cpp/function-call-from-object-file/lib2.cpp
new file mode 100644
index 000000000000..2f9d81a8bdf4
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/lib2.cpp
@@ -0,0 +1,6 @@
+#include "common.h"
+
+void lib2_func(Foo *) {
+ // Force definition into lib2.o debug-info.
+ Foo{}.foo();
+}
diff --git a/lldb/test/API/lang/cpp/function-call-from-object-file/main.cpp b/lldb/test/API/lang/cpp/function-call-from-object-file/main.cpp
new file mode 100644
index 000000000000..61ca798daf1d
--- /dev/null
+++ b/lldb/test/API/lang/cpp/function-call-from-object-file/main.cpp
@@ -0,0 +1,10 @@
+struct Foo;
+
+extern void lib1_func(Foo *);
+extern void lib2_func(Foo *);
+
+int main() {
+ lib1_func(nullptr);
+ lib2_func(nullptr);
+ return 0;
+}
diff --git a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
index 5f939ecfbef2..882c91d1ce8c 100644
--- a/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
+++ b/lldb/test/API/lang/cpp/structured-binding/TestStructuredBinding.py
@@ -99,16 +99,21 @@ class TestStructuredBinding(TestBase):
self.expect_expr("ty2", result_value="'z'")
self.expect_expr("tz2", result_value="10")
- self.expect(
- "frame variable",
- substrs=[
- "tx1 =",
- "ty1 =",
- "tz1 =",
- "tx2 =",
- "ty2 =",
- "tz2 =",
- "mp1 =",
- "mp2 =",
- ],
- )
+ # Older versions of Clang marked structured binding variables
+ # as artificial, and thus LLDB wouldn't display them.
+ if self.expectedCompiler(["clang"]) and self.expectedCompilerVersion(
+ [">=", "22.0"]
+ ):
+ self.expect(
+ "frame variable",
+ substrs=[
+ "tx1 =",
+ "ty1 =",
+ "tz1 =",
+ "tx2 =",
+ "ty2 =",
+ "tz2 =",
+ "mp1 =",
+ "mp2 =",
+ ],
+ )
diff --git a/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py b/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py
index eac7b5ef1099..83c057220410 100644
--- a/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py
+++ b/lldb/test/API/lang/cpp/template-arguments/TestCppTemplateArguments.py
@@ -1,4 +1,5 @@
import lldb
+import lldbsuite.test.lldbplatformutil as lldbplatformutil
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
@@ -82,8 +83,12 @@ class TestCase(TestBase):
value = self.expect_expr("temp7", result_type="Foo<__fp16, __fp16>")
self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
- value = self.expect_expr("temp8", result_type="Foo<__fp16, __fp16>")
- self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
+ # The target we use when evaluating these expressions for Arm leads to there
+ # not being a __bf16 type in the AST so we fall back to __fp16 and evaluating
+ # this fails.
+ if lldbplatformutil.getArchitecture() != "arm":
+ value = self.expect_expr("temp8", result_type="Foo<__bf16, __bf16>")
+ self.assertFalse(value.GetType().GetTemplateArgumentValue(target, 1))
value = self.expect_expr("temp9", result_type="Bar<double, 1.200000e+00>")
template_param_value = value.GetType().GetTemplateArgumentValue(target, 1)
diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile b/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile
new file mode 100644
index 000000000000..c7947fc138c1
--- /dev/null
+++ b/lldb/test/API/lang/objc/ivar-in-framework-base/Makefile
@@ -0,0 +1,6 @@
+OBJC_SOURCES := main.m lib.m
+LD_EXTRAS = -framework Foundation
+
+include Makefile.rules
+
+lib.o: CFLAGS = $(CFLAGS_NO_DEBUG)
diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py b/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py
new file mode 100644
index 000000000000..40fc6b7a6899
--- /dev/null
+++ b/lldb/test/API/lang/objc/ivar-in-framework-base/TestIvarInFrameworkBase.py
@@ -0,0 +1,39 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestIvarInFrameworkBase(TestBase):
+ """
+ Tests whether LLDB's data inspection commands can correctly retrieve
+ information about ivars from the Objective-C runtime.
+ In this test-case we have a base class type for which we don't have access
+ to the debug-info of the implementation (mimicking the scenario of subclassing
+ a type from a system framework). LLDB won't be able to see the backing ivar for
+ 'fooProp' from just debug-info, but it will fall back on the runtime to get the
+ necessary information.
+ """
+
+ def test_frame_var(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
+ self.expect("frame variable *bar", substrs=["_fooProp = 10", "_barProp = 15"])
+
+ def test_expr(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
+ self.expect_expr(
+ "*bar",
+ result_type="Bar",
+ result_children=[
+ ValueCheck(
+ name="Foo",
+ children=[
+ ValueCheck(name="NSObject"),
+ ValueCheck(name="_fooProp", value="10"),
+ ],
+ ),
+ ValueCheck(name="_barProp", value="15"),
+ ],
+ )
diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h
new file mode 100644
index 000000000000..31ceb53dc688
--- /dev/null
+++ b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.h
@@ -0,0 +1,6 @@
+#import <Foundation/Foundation.h>
+
+@interface Foo : NSObject
+@property int fooProp;
+- (id)init;
+@end
diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m
new file mode 100644
index 000000000000..e1bf80ac4bd4
--- /dev/null
+++ b/lldb/test/API/lang/objc/ivar-in-framework-base/lib.m
@@ -0,0 +1,8 @@
+#import "lib.h"
+
+@implementation Foo
+- (id)init {
+ self.fooProp = 10;
+ return self;
+}
+@end
diff --git a/lldb/test/API/lang/objc/ivar-in-framework-base/main.m b/lldb/test/API/lang/objc/ivar-in-framework-base/main.m
new file mode 100644
index 000000000000..1fd352ec92c5
--- /dev/null
+++ b/lldb/test/API/lang/objc/ivar-in-framework-base/main.m
@@ -0,0 +1,22 @@
+#import "lib.h"
+#include <stdio.h>
+
+@interface Bar : Foo
+@property int barProp;
+- (id)init;
+@end
+
+@implementation Bar
+
+- (id)init {
+ self = [super init];
+ self.barProp = 15;
+ return self;
+}
+@end
+
+int main() {
+ Bar *bar = [Bar new];
+ puts("break here");
+ return 0;
+}
diff --git a/lldb/test/API/macosx/debugserver-multimemread/Makefile b/lldb/test/API/macosx/debugserver-multimemread/Makefile
new file mode 100644
index 000000000000..10495940055b
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py b/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py
new file mode 100644
index 000000000000..6caa5f85c284
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/TestDebugserverMultiMemRead.py
@@ -0,0 +1,102 @@
+"""
+Tests debugserver support for MultiMemRead.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+@skipUnlessDarwin
+@skipIfOutOfTreeDebugserver
+class TestCase(TestBase):
+ def send_process_packet(self, packet_str):
+ self.runCmd(f"proc plugin packet send {packet_str}", check=False)
+ # The output is of the form:
+ # packet: <packet_str>
+ # response: <response>
+ reply = self.res.GetOutput().split("\n")
+ packet = reply[0].strip()
+ response = reply[1].strip()
+
+ self.assertTrue(packet.startswith("packet: "))
+ self.assertTrue(response.startswith("response: "))
+ return response[len("response: ") :]
+
+ def check_invalid_packet(self, packet_str):
+ reply = self.send_process_packet("packet_str")
+ self.assertEqual(reply, "E03")
+
+ def test_packets(self):
+ self.build()
+ source_file = lldb.SBFileSpec("main.c")
+ target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+ self, "break here", source_file
+ )
+
+ reply = self.send_process_packet("qSupported")
+ self.assertIn("MultiMemRead+", reply)
+
+ mem_address_var = thread.frames[0].FindVariable("memory")
+ self.assertTrue(mem_address_var)
+ mem_address = mem_address_var.GetValueAsUnsigned() + 42
+
+ # no ":"
+ self.check_invalid_packet("MultiMemRead")
+ # missing ranges
+ self.check_invalid_packet("MultiMemRead:")
+ # needs at least one range
+ self.check_invalid_packet("MultiMemRead:ranges:")
+ # needs at least one range
+ self.check_invalid_packet("MultiMemRead:ranges:,")
+ # a range is a pair of numbers
+ self.check_invalid_packet("MultiMemRead:ranges:10")
+ # a range is a pair of numbers
+ self.check_invalid_packet("MultiMemRead:ranges:10,")
+ # range list must end with ;
+ self.check_invalid_packet("MultiMemRead:ranges:10,2")
+ self.check_invalid_packet("MultiMemRead:ranges:10,2,")
+ self.check_invalid_packet("MultiMemRead:ranges:10,2,3")
+ # ranges are pairs of numbers.
+ self.check_invalid_packet("MultiMemRead:ranges:10,2,3;")
+ # unrecognized field
+ self.check_invalid_packet("MultiMemRead:ranges:10,2;blah:;")
+ # unrecognized field
+ self.check_invalid_packet("MultiMemRead:blah:;ranges:10,2;")
+
+ # Zero-length reads are ok.
+ reply = self.send_process_packet("MultiMemRead:ranges:0,0;")
+ self.assertEqual(reply, "0;")
+
+ # Debugserver is permissive with trailing commas.
+ reply = self.send_process_packet("MultiMemRead:ranges:10,2,;")
+ self.assertEqual(reply, "0;")
+ reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},2,;")
+ self.assertEqual(reply, "2;ab")
+
+ reply = self.send_process_packet("MultiMemRead:ranges:10,2;")
+ self.assertEqual(reply, "0;")
+ reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},0;")
+ self.assertEqual(reply, "0;")
+ reply = self.send_process_packet(f"MultiMemRead:ranges:{mem_address:x},2;")
+ self.assertEqual(reply, "2;ab")
+ reply = self.send_process_packet(
+ f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4;"
+ )
+ self.assertEqual(reply, "2,4;abcdef")
+ reply = self.send_process_packet(
+ f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4,{mem_address+6:x},8;"
+ )
+ self.assertEqual(reply, "2,4,8;abcdefghijklmn")
+
+ # Test zero length in the middle.
+ reply = self.send_process_packet(
+ f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},0,{mem_address+6:x},8;"
+ )
+ self.assertEqual(reply, "2,0,8;abghijklmn")
+ # Test zero length in the end.
+ reply = self.send_process_packet(
+ f"MultiMemRead:ranges:{mem_address:x},2,{mem_address+2:x},4,{mem_address+6:x},0;"
+ )
+ self.assertEqual(reply, "2,4,0;abcdef")
diff --git a/lldb/test/API/macosx/debugserver-multimemread/main.c b/lldb/test/API/macosx/debugserver-multimemread/main.c
new file mode 100644
index 000000000000..44cdd475b550
--- /dev/null
+++ b/lldb/test/API/macosx/debugserver-multimemread/main.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char **argv) {
+ char *memory = malloc(1024);
+ memset(memory, '-', 1024);
+ // Write "interesting" characters at an offset from the memory filled with
+ // `-`. This way, if we read outside the range in either direction, we should
+ // find `-`s`.
+ int offset = 42;
+ for (int i = offset; i < offset + 14; i++)
+ memory[i] = 'a' + (i - offset);
+ return 0; // break here
+}
diff --git a/lldb/test/API/macosx/mte/Makefile b/lldb/test/API/macosx/mte/Makefile
new file mode 100644
index 000000000000..d614e0f0a3bc
--- /dev/null
+++ b/lldb/test/API/macosx/mte/Makefile
@@ -0,0 +1,15 @@
+C_SOURCES := main.c
+
+EXE := uaf
+
+binary-plain: uaf
+binary-entitled: uaf sign
+
+all: binary-entitled
+
+include Makefile.rules
+
+sign: mte-entitlements.plist uaf
+ifeq ($(OS),Darwin)
+ codesign -s - -f --entitlements $^
+endif
diff --git a/lldb/test/API/macosx/mte/TestDarwinMTE.py b/lldb/test/API/macosx/mte/TestDarwinMTE.py
new file mode 100644
index 000000000000..a70b4b4aed26
--- /dev/null
+++ b/lldb/test/API/macosx/mte/TestDarwinMTE.py
@@ -0,0 +1,122 @@
+"""Test MTE Memory Tagging on Apple platforms"""
+
+import lldb
+import re
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbsuite.test.cpu_feature as cpu_feature
+
+exe_name = "uaf" # Must match Makefile
+
+
+class TestDarwinMTE(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_process_launch_memory_tagging(self):
+ self.build(make_targets=["binary-plain"])
+ self.createTestTarget(self.getBuildArtifact(exe_name))
+
+ self.expect("process launch", substrs=["exited with status = 0"])
+
+ self.expect(
+ "process launch --memory-tagging",
+ substrs=["stopped", "stop reason = EXC_ARM_MTE_TAG_FAULT"],
+ )
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_tag_fault(self):
+ self.build()
+ exe = self.getBuildArtifact(exe_name)
+
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target, VALID_TARGET)
+
+ process = target.LaunchSimple(None, None, None)
+ self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
+
+ self.expect(
+ "thread info",
+ substrs=[
+ "stop reason = EXC_ARM_MTE_TAG_FAULT",
+ "MTE tag mismatch detected",
+ ],
+ )
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_region(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory region ptr
+ # [0x00000001005ec000-0x00000001009ec000) rw-
+ # memory tagging: enabled
+ # Modified memory (dirty) page list provided, 2 entries.
+ # Dirty pages: 0x1005ec000, 0x1005fc000.
+ self.expect("memory region ptr", substrs=["memory tagging: enabled"])
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_read_show_tags(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory read ptr-16 ptr+48 --show-tags
+ # 0x7d2c00930: 00 00 00 00 00 00 00 00 d0 e3 a5 0a 02 00 00 00 ................ (tag: 0x3)
+ # 0x7d2c00940: 48 65 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 Hello........... (tag: 0xb)
+ # 0x7d2c00950: 57 6f 72 6c 64 00 00 00 00 00 00 00 00 00 00 00 World........... (tag: 0xb)
+ # 0x7d2c00960: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ (tag: 0x9)
+ self.expect(
+ "memory read ptr-16 ptr+48 --show-tags",
+ substrs=[" Hello...........", " World..........."],
+ patterns=[r"(.*\(tag: 0x[0-9a-f]\)\n){4}"],
+ )
+
+ def _parse_pointer_tag(self, output):
+ return re.search(r"Logical tag: (0x[0-9a-f])", output).group(1)
+
+ def _parse_memory_tags(self, output, expected_tag_count):
+ tags = re.findall(r"\): (0x[0-9a-f])", output)
+ self.assertEqual(len(tags), expected_tag_count)
+ return tags
+
+ @skipUnlessFeature(cpu_feature.AArch64.MTE)
+ def test_memory_tag_read(self):
+ self.build()
+ lldbutil.run_to_source_breakpoint(
+ self, "// before free", lldb.SBFileSpec("main.c"), exe_name=exe_name
+ )
+
+ # (lldb) memory tag read ptr-1 ptr+33
+ # Logical tag: 0x5
+ # Allocation tags:
+ # [0x100a65a40, 0x100a65a50): 0xf (mismatch)
+ # [0x100a65a50, 0x100a65a60): 0x5
+ # [0x100a65a60, 0x100a65a70): 0x5
+ # [0x100a65a70, 0x100a65a80): 0x2 (mismatch)
+ self.expect(
+ "memory tag read ptr-1 ptr+33",
+ substrs=["Logical tag: 0x", "Allocation tags:", "(mismatch)"],
+ patterns=[r"(\[.*\): 0x[0-9a-f].*\n){4}"],
+ )
+ output = self.res.GetOutput()
+ self.assertEqual(output.count("(mismatch)"), 2)
+ ptr_tag = self._parse_pointer_tag(output)
+ tags = self._parse_memory_tags(output, 4)
+ self.assertEqual(tags[1], ptr_tag)
+ self.assertEqual(tags[2], ptr_tag)
+ self.assertNotEqual(tags[0], ptr_tag) # Memory that comes before/after
+ self.assertNotEqual(tags[3], ptr_tag) # allocation has different tag.
+
+ # Continue running until MTE fault
+ self.expect("process continue", substrs=["stop reason = EXC_ARM_MTE_TAG_FAULT"])
+
+ self.runCmd("memory tag read ptr-1 ptr+33")
+ output = self.res.GetOutput()
+ self.assertEqual(output.count("(mismatch)"), 4)
+ tags = self._parse_memory_tags(output, 4)
+ self.assertTrue(all(t != ptr_tag for t in tags))
diff --git a/lldb/test/API/macosx/mte/main.c b/lldb/test/API/macosx/mte/main.c
new file mode 100644
index 000000000000..f9f6b1594ef4
--- /dev/null
+++ b/lldb/test/API/macosx/mte/main.c
@@ -0,0 +1,28 @@
+#include <malloc/malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Produce some names on the trace
+const size_t tag_granule = 16;
+static uint8_t *my_malloc(void) { return malloc(2 * tag_granule); }
+static uint8_t *allocate(void) { return my_malloc(); }
+
+static void my_free(void *ptr) { free(ptr); }
+static void deallocate(void *ptr) { my_free(ptr); }
+
+static void touch_memory(uint8_t *ptr) { ptr[7] = 1; } // invalid access
+static void modify(uint8_t *ptr) { touch_memory(ptr); }
+
+int main() {
+ uint8_t *ptr = allocate();
+
+ strncpy((char *)ptr, "Hello", 16);
+ strncpy((char *)ptr + 16, "World", 16);
+
+ deallocate(ptr); // before free
+
+ modify(ptr); // use-after-free
+
+ return 0;
+}
diff --git a/lldb/test/API/macosx/mte/mte-entitlements.plist b/lldb/test/API/macosx/mte/mte-entitlements.plist
new file mode 100644
index 000000000000..6de5d5634d87
--- /dev/null
+++ b/lldb/test/API/macosx/mte/mte-entitlements.plist
@@ -0,0 +1,10 @@
+<?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>com.apple.security.hardened-process</key>
+ <true/>
+ <key>com.apple.security.hardened-process.checked-allocations</key>
+ <true/>
+</dict>
+</plist>
diff --git a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
index 43f45f330ee2..44b118328801 100644
--- a/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
+++ b/lldb/test/API/python_api/debugger/TestDebuggerAPI.py
@@ -294,3 +294,150 @@ class DebuggerAPITestCase(TestBase):
self.assertEqual(instance_str, class_str)
self.assertEqual(class_str, property_str)
+
+ def test_find_target_with_unique_id(self):
+ """Test SBDebugger.FindTargetByGloballyUniqueID() functionality."""
+
+ # Test with invalid ID - should return invalid target
+ invalid_target = self.dbg.FindTargetByGloballyUniqueID(999999)
+ self.assertFalse(invalid_target.IsValid())
+
+ # Test with ID 0 - should return invalid target
+ zero_target = self.dbg.FindTargetByGloballyUniqueID(0)
+ self.assertFalse(zero_target.IsValid())
+
+ # Build a real executable and create target with it
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target.IsValid())
+
+ # Find the target using its unique ID
+ unique_id = target.GetGloballyUniqueID()
+ self.assertNotEqual(unique_id, lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID)
+ found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id)
+ self.assertTrue(found_target.IsValid())
+ self.assertEqual(
+ self.dbg.GetIndexOfTarget(target), self.dbg.GetIndexOfTarget(found_target)
+ )
+ self.assertEqual(found_target.GetGloballyUniqueID(), unique_id)
+
+ def test_target_unique_id_uniqueness(self):
+ """Test that Target.GetGloballyUniqueID() returns unique values across multiple targets."""
+
+ # Create multiple targets and verify they all have unique IDs
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ targets = []
+ unique_ids = set()
+
+ for i in range(10):
+ target = self.dbg.CreateTarget(exe)
+ self.assertTrue(target.IsValid())
+
+ unique_id = target.GetGloballyUniqueID()
+ self.assertNotEqual(unique_id, 0)
+
+ # Verify this ID hasn't been used before
+ self.assertNotIn(
+ unique_id, unique_ids, f"Duplicate unique ID found: {unique_id}"
+ )
+
+ unique_ids.add(unique_id)
+ targets.append(target)
+
+ # Verify all targets can still be found by their IDs
+ for target in targets:
+ unique_id = target.GetGloballyUniqueID()
+ found = self.dbg.FindTargetByGloballyUniqueID(unique_id)
+ self.assertTrue(found.IsValid())
+ self.assertEqual(found.GetGloballyUniqueID(), unique_id)
+
+ def test_target_unique_id_uniqueness_after_deletion(self):
+ """Test finding targets have unique ID after target deletion."""
+ # Create two targets
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+ target1 = self.dbg.CreateTarget(exe)
+ target2 = self.dbg.CreateTarget(exe)
+ self.assertTrue(target1.IsValid())
+ self.assertTrue(target2.IsValid())
+
+ unique_id1 = target1.GetGloballyUniqueID()
+ unique_id2 = target2.GetGloballyUniqueID()
+ self.assertNotEqual(unique_id1, 0)
+ self.assertNotEqual(unique_id2, 0)
+ self.assertNotEqual(unique_id1, unique_id2)
+
+ # Verify we can find them initially
+ found_target1 = self.dbg.FindTargetByGloballyUniqueID(unique_id1)
+ found_target2 = self.dbg.FindTargetByGloballyUniqueID(unique_id2)
+ self.assertTrue(found_target1.IsValid())
+ self.assertTrue(found_target2.IsValid())
+ target2_index = self.dbg.GetIndexOfTarget(target2)
+
+ # Delete target 2
+ deleted = self.dbg.DeleteTarget(target2)
+ self.assertTrue(deleted)
+
+ # Try to find the deleted target - should not be found
+ not_found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id2)
+ self.assertFalse(not_found_target.IsValid())
+
+ # Create a new target
+ target3 = self.dbg.CreateTarget(exe)
+ self.assertTrue(target3.IsValid())
+ # Target list index of target3 should be the same as target2's
+ # since it was deleted, but it should have a distinct unique ID
+ target3_index = self.dbg.GetIndexOfTarget(target3)
+ unique_id3 = target3.GetGloballyUniqueID()
+ self.assertEqual(target3_index, target2_index)
+ self.assertNotEqual(unique_id3, unique_id2)
+ self.assertNotEqual(unique_id3, unique_id1)
+ # Make sure we can find the new target
+ found_target3 = self.dbg.FindTargetByGloballyUniqueID(
+ target3.GetGloballyUniqueID()
+ )
+ self.assertTrue(found_target3.IsValid())
+
+ def test_target_globally_unique_id_across_debuggers(self):
+ """Test that target IDs are globally unique across multiple debuggers."""
+ self.build()
+ exe = self.getBuildArtifact("a.out")
+
+ # Create two debuggers with targets each
+ debugger1 = lldb.SBDebugger.Create()
+ debugger2 = lldb.SBDebugger.Create()
+ self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger1))
+ self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger2))
+
+ # Create 2 targets per debugger
+ targets_d1 = [debugger1.CreateTarget(exe), debugger1.CreateTarget(exe)]
+ targets_d2 = [debugger2.CreateTarget(exe), debugger2.CreateTarget(exe)]
+ targets = targets_d1 + targets_d2
+
+ # Get all IDs and verify they're unique
+ ids = [target.GetGloballyUniqueID() for target in targets]
+ self.assertEqual(
+ len(set(ids)), len(ids), f"IDs should be globally unique: {ids}"
+ )
+ self.assertTrue(
+ all(uid != lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID for uid in ids),
+ "All IDs should be valid",
+ )
+
+ # Verify targets can be found by their IDs in respective debuggers
+ for debugger, target_pair in [
+ (debugger1, targets[:2]),
+ (debugger2, targets[2:]),
+ ]:
+ for target in target_pair:
+ found = debugger.FindTargetByGloballyUniqueID(
+ target.GetGloballyUniqueID()
+ )
+ self.assertTrue(
+ found.IsValid(), "Target should be found by its unique ID"
+ )
+ self.assertEqual(
+ found.GetGloballyUniqueID(), target.GetGloballyUniqueID()
+ )
diff --git a/lldb/test/API/python_api/default-constructor/sb_filespec.py b/lldb/test/API/python_api/default-constructor/sb_filespec.py
index 4ab5c49c37eb..5dd78b1ace98 100644
--- a/lldb/test/API/python_api/default-constructor/sb_filespec.py
+++ b/lldb/test/API/python_api/default-constructor/sb_filespec.py
@@ -10,5 +10,5 @@ def fuzz_obj(obj):
obj.ResolveExecutableLocation()
obj.GetFilename()
obj.GetDirectory()
- obj.GetPath(None, 0)
+ obj.GetPath(1)
obj.GetDescription(lldb.SBStream())
diff --git a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
index 1029bdc3096d..01ed11a5a112 100644
--- a/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
+++ b/lldb/test/API/python_api/interpreter/TestCommandInterpreterAPI.py
@@ -1,4 +1,4 @@
-"""Test the SBCommandInterpreter APIs."""
+"""tESt the SBCommandInterpreter APIs."""
import json
import lldb
@@ -156,13 +156,15 @@ class CommandInterpreterAPICase(TestBase):
self.assertEqual(transcript[0]["error"], "")
# (lldb) an-unknown-command
- self.assertEqual(transcript[1],
+ self.assertEqual(
+ transcript[1],
{
"command": "an-unknown-command",
# Unresolved commands don't have "commandName"/"commandArguments"
"output": "",
"error": "error: 'an-unknown-command' is not a valid command.\n",
- })
+ },
+ )
# (lldb) br s -f main.c -l <line>
self.assertEqual(transcript[2]["command"], "br s -f main.c -l %d" % self.line)
@@ -175,14 +177,17 @@ class CommandInterpreterAPICase(TestBase):
self.assertEqual(transcript[2]["error"], "")
# (lldb) p a
- self.assertEqual(transcript[3],
+ self.assertEqual(
+ transcript[3],
{
"command": "p a",
"commandName": "dwim-print",
"commandArguments": "-- a",
"output": "",
- "error": "error: <user expression 0>:1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n",
- })
+ "error": "note: Falling back to default language. Ran expression as 'Objective C++'.\n"
+ "error: <user expression 0>:1:1: use of undeclared identifier 'a'\n 1 | a\n | ^\n",
+ },
+ )
# (lldb) statistics dump
self.assertEqual(transcript[4]["command"], "statistics dump")
@@ -203,7 +208,10 @@ class CommandInterpreterAPICase(TestBase):
self.assertTrue(ci, VALID_COMMAND_INTERPRETER)
# The setting's default value should be "false"
- self.runCmd("settings show interpreter.save-transcript", "interpreter.save-transcript (boolean) = false\n")
+ self.runCmd(
+ "settings show interpreter.save-transcript",
+ "interpreter.save-transcript (boolean) = false\n",
+ )
def test_save_transcript_setting_off(self):
ci = self.dbg.GetCommandInterpreter()
@@ -250,17 +258,37 @@ class CommandInterpreterAPICase(TestBase):
structured_data_1 = ci.GetTranscript()
self.assertTrue(structured_data_1.IsValid())
self.assertEqual(structured_data_1.GetSize(), 1)
- self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
+ self.assertEqual(
+ structured_data_1.GetItemAtIndex(0)
+ .GetValueForKey("command")
+ .GetStringValue(100),
+ "version",
+ )
# Run some more commands and get the transcript as structured data again
self.runCmd("help")
structured_data_2 = ci.GetTranscript()
self.assertTrue(structured_data_2.IsValid())
self.assertEqual(structured_data_2.GetSize(), 2)
- self.assertEqual(structured_data_2.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
- self.assertEqual(structured_data_2.GetItemAtIndex(1).GetValueForKey("command").GetStringValue(100), "help")
+ self.assertEqual(
+ structured_data_2.GetItemAtIndex(0)
+ .GetValueForKey("command")
+ .GetStringValue(100),
+ "version",
+ )
+ self.assertEqual(
+ structured_data_2.GetItemAtIndex(1)
+ .GetValueForKey("command")
+ .GetStringValue(100),
+ "help",
+ )
# Now, the first structured data should remain unchanged
self.assertTrue(structured_data_1.IsValid())
self.assertEqual(structured_data_1.GetSize(), 1)
- self.assertEqual(structured_data_1.GetItemAtIndex(0).GetValueForKey("command").GetStringValue(100), "version")
+ self.assertEqual(
+ structured_data_1.GetItemAtIndex(0)
+ .GetValueForKey("command")
+ .GetStringValue(100),
+ "version",
+ )
diff --git a/lldb/test/API/python_api/sbtype_basic_type/TestSBTypeBasicType.py b/lldb/test/API/python_api/sbtype_basic_type/TestSBTypeBasicType.py
index 535d8b900673..f8594dfc6b78 100644
--- a/lldb/test/API/python_api/sbtype_basic_type/TestSBTypeBasicType.py
+++ b/lldb/test/API/python_api/sbtype_basic_type/TestSBTypeBasicType.py
@@ -28,3 +28,11 @@ class TestCase(TestBase):
self.assertEqual(b.GetType().GetBasicType(), int_basic_type)
self.assertEqual(c.GetType().GetBasicType(), int_basic_type)
self.assertEqual(d.GetType().GetBasicType(), int_basic_type)
+
+ # Check the size of the chosen basic types.
+ self.assertEqual(self.target().FindFirstType("__int128").size, 16)
+ self.assertEqual(self.target().FindFirstType("unsigned __int128").size, 16)
+
+ # Check the size of the chosen aliases of basic types.
+ self.assertEqual(self.target().FindFirstType("__int128_t").size, 16)
+ self.assertEqual(self.target().FindFirstType("__uint128_t").size, 16)
diff --git a/lldb/test/API/tools/lldb-dap/attach-commands/Makefile b/lldb/test/API/tools/lldb-dap/attach-commands/Makefile
new file mode 100644
index 000000000000..10495940055b
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/attach-commands/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/tools/lldb-dap/attach-commands/TestDAP_attachCommands.py b/lldb/test/API/tools/lldb-dap/attach-commands/TestDAP_attachCommands.py
new file mode 100644
index 000000000000..9e29f07db80f
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/attach-commands/TestDAP_attachCommands.py
@@ -0,0 +1,145 @@
+"""
+Test lldb-dap attach commands
+"""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import lldbdap_testcase
+import time
+
+
+class TestDAP_attachCommands(lldbdap_testcase.DAPTestCaseBase):
+ @skipIfNetBSD # Hangs on NetBSD as well
+ def test_commands(self):
+ """
+ Tests the "initCommands", "preRunCommands", "stopCommands",
+ "exitCommands", "terminateCommands" and "attachCommands"
+ that can be passed during attach.
+
+ "initCommands" are a list of LLDB commands that get executed
+ before the target is created.
+ "preRunCommands" are a list of LLDB commands that get executed
+ after the target has been created and before the launch.
+ "stopCommands" are a list of LLDB commands that get executed each
+ time the program stops.
+ "exitCommands" are a list of LLDB commands that get executed when
+ the process exits
+ "attachCommands" are a list of LLDB commands that get executed and
+ must have a valid process in the selected target in LLDB after
+ they are done executing. This allows custom commands to create any
+ kind of debug session.
+ "terminateCommands" are a list of LLDB commands that get executed when
+ the debugger session terminates.
+ """
+ program = self.build_and_create_debug_adapter_for_attach()
+
+ # Here we just create a target and launch the process as a way to test
+ # if we are able to use attach commands to create any kind of a target
+ # and use it for debugging
+ attachCommands = [
+ 'target create -d "%s"' % (program),
+ "process launch --stop-at-entry",
+ ]
+ initCommands = ["target list", "platform list"]
+ preRunCommands = ["image list a.out", "image dump sections a.out"]
+ postRunCommands = ["help trace", "help process trace"]
+ stopCommands = ["frame variable", "thread backtrace"]
+ exitCommands = ["expr 2+3", "expr 3+4"]
+ terminateCommands = ["expr 4+2"]
+ self.attach(
+ program=program,
+ attachCommands=attachCommands,
+ initCommands=initCommands,
+ preRunCommands=preRunCommands,
+ stopCommands=stopCommands,
+ exitCommands=exitCommands,
+ terminateCommands=terminateCommands,
+ postRunCommands=postRunCommands,
+ )
+ # Get output from the console. This should contain both the
+ # "initCommands" and the "preRunCommands".
+ output = self.get_console()
+ # Verify all "initCommands" were found in console output
+ self.verify_commands("initCommands", output, initCommands)
+ # Verify all "preRunCommands" were found in console output
+ self.verify_commands("preRunCommands", output, preRunCommands)
+ # Verify all "postRunCommands" were found in console output
+ self.verify_commands("postRunCommands", output, postRunCommands)
+
+ functions = ["main"]
+ 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(pattern=stopCommands[-1])
+ self.verify_commands("stopCommands", output, stopCommands)
+
+ # Continue after launch and hit the "pause()" call and stop the target.
+ # Get output from the console. This should contain both the
+ # "stopCommands" that were run after we stop.
+ self.do_continue()
+ time.sleep(0.5)
+ self.dap_server.request_pause()
+ self.dap_server.wait_for_stopped()
+ output = self.collect_console(pattern=stopCommands[-1])
+ 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
+ # and the "terminateCommands" due to the debugging session ending
+ output = self.collect_console(
+ pattern=terminateCommands[0],
+ )
+ self.verify_commands("exitCommands", output, exitCommands)
+ self.verify_commands("terminateCommands", output, terminateCommands)
+
+ def test_attach_command_process_failures(self):
+ """
+ Tests that a 'attachCommands' is expected to leave the debugger's
+ selected target with a valid process.
+ """
+ program = self.build_and_create_debug_adapter_for_attach()
+ attachCommands = ['script print("oops, forgot to attach to a process...")']
+ resp = self.attach(
+ program=program,
+ attachCommands=attachCommands,
+ expectFailure=True,
+ )
+ self.assertFalse(resp["success"])
+ self.assertIn(
+ "attachCommands failed to attach to a process",
+ resp["body"]["error"]["format"],
+ )
+
+ @skipIfNetBSD # Hangs on NetBSD as well
+ def test_terminate_commands(self):
+ """
+ Tests that the "terminateCommands", that can be passed during
+ attach, are run when the debugger is disconnected.
+ """
+ program = self.build_and_create_debug_adapter_for_attach()
+
+ # Here we just create a target and launch the process as a way to test
+ # if we are able to use attach commands to create any kind of a target
+ # and use it for debugging
+ attachCommands = [
+ 'target create -d "%s"' % (program),
+ "process launch --stop-at-entry",
+ ]
+ terminateCommands = ["expr 4+2"]
+ self.attach(
+ program=program,
+ attachCommands=attachCommands,
+ terminateCommands=terminateCommands,
+ disconnectAutomatically=False,
+ )
+ self.get_console()
+ # Once it's disconnected the console should contain the
+ # "terminateCommands"
+ self.dap_server.request_disconnect(terminateDebuggee=True)
+ output = self.collect_console(
+ pattern=terminateCommands[0],
+ )
+ self.verify_commands("terminateCommands", output, terminateCommands)
diff --git a/lldb/test/API/tools/lldb-dap/attach-commands/main.c b/lldb/test/API/tools/lldb-dap/attach-commands/main.c
new file mode 100644
index 000000000000..f56d5d53afa0
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/attach-commands/main.c
@@ -0,0 +1,30 @@
+#include "attach.h"
+#include <stdio.h>
+#ifdef _WIN32
+#include <process.h>
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+int main(int argc, char const *argv[]) {
+ lldb_enable_attach();
+
+ if (argc >= 2) {
+ // Create the synchronization token.
+ FILE *f = fopen(argv[1], "wx");
+ if (!f)
+ return 1;
+ fputs("\n", f);
+ fflush(f);
+ fclose(f);
+ }
+
+ printf("pid = %i\n", getpid());
+#ifdef _WIN32
+ Sleep(10 * 1000);
+#else
+ sleep(10);
+#endif
+ return 0; // breakpoint 1
+}
diff --git a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
index c54e21c1b973..5331a9f94ef1 100644
--- a/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
+++ b/lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
@@ -11,50 +11,35 @@ import threading
import time
-def spawn_and_wait(program, delay):
- if delay:
- time.sleep(delay)
- process = subprocess.Popen(
- [program], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
- )
- process.wait()
-
-
class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
- def set_and_hit_breakpoint(self, continueToExit=True):
- source = "main.c"
- breakpoint1_line = line_number(source, "// breakpoint 1")
- lines = [breakpoint1_line]
- # Set breakpoint in the thread function so we can step the threads
- breakpoint_ids = self.set_source_breakpoints(source, lines)
- self.assertEqual(
- len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
- )
- # Test binary will sleep for 10s, offset the breakpoint timeout
- # accordingly.
- timeout_offset = 10
- self.continue_to_breakpoints(
- breakpoint_ids, timeout=timeout_offset + self.DEFAULT_TIMEOUT
+ def spawn(self, args):
+ self.process = subprocess.Popen(
+ args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
)
- if continueToExit:
- self.continue_to_exit()
- @skipIfNetBSD # Hangs on NetBSD as well
+ def spawn_and_wait(self, program, delay):
+ time.sleep(delay)
+ self.spawn([program])
+ self.process.wait()
+
+ def continue_and_verify_pid(self):
+ self.do_continue()
+ out, _ = self.process.communicate("foo")
+ self.assertIn(f"pid = {self.process.pid}", out)
+
def test_by_pid(self):
"""
Tests attaching to a process by process ID.
"""
program = self.build_and_create_debug_adapter_for_attach()
- self.process = subprocess.Popen(
- [program],
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
+ self.spawn([program])
self.attach(pid=self.process.pid)
- self.set_and_hit_breakpoint(continueToExit=True)
+ self.continue_and_verify_pid()
- @skipIfNetBSD # Hangs on NetBSD as well
def test_by_name(self):
"""
Tests attaching to a process by process name.
@@ -65,24 +50,20 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
pid_file_path = lldbutil.append_to_process_working_directory(
self, "pid_file_%d" % (int(time.time()))
)
-
- popen = self.spawnSubprocess(program, [pid_file_path])
+ self.spawn([program, pid_file_path])
lldbutil.wait_for_file_on_target(self, pid_file_path)
self.attach(program=program)
- self.set_and_hit_breakpoint(continueToExit=True)
+ self.continue_and_verify_pid()
- @skipUnlessDarwin
- @skipIfNetBSD # Hangs on NetBSD as well
def test_by_name_waitFor(self):
"""
- Tests attaching to a process by process name and waiting for the
- next instance of a process to be launched, ignoring all current
- ones.
+ Tests waiting for, and attaching to a process by process name that
+ doesn't exist yet.
"""
program = self.build_and_create_debug_adapter_for_attach()
self.spawn_thread = threading.Thread(
- target=spawn_and_wait,
+ target=self.spawn_and_wait,
args=(
program,
1.0,
@@ -90,140 +71,4 @@ class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
)
self.spawn_thread.start()
self.attach(program=program, waitFor=True)
- self.set_and_hit_breakpoint(continueToExit=True)
-
- @skipIfNetBSD # Hangs on NetBSD as well
- def test_commands(self):
- """
- Tests the "initCommands", "preRunCommands", "stopCommands",
- "exitCommands", "terminateCommands" and "attachCommands"
- that can be passed during attach.
-
- "initCommands" are a list of LLDB commands that get executed
- before the target is created.
- "preRunCommands" are a list of LLDB commands that get executed
- after the target has been created and before the launch.
- "stopCommands" are a list of LLDB commands that get executed each
- time the program stops.
- "exitCommands" are a list of LLDB commands that get executed when
- the process exits
- "attachCommands" are a list of LLDB commands that get executed and
- must have a valid process in the selected target in LLDB after
- they are done executing. This allows custom commands to create any
- kind of debug session.
- "terminateCommands" are a list of LLDB commands that get executed when
- the debugger session terminates.
- """
- program = self.build_and_create_debug_adapter_for_attach()
-
- # Here we just create a target and launch the process as a way to test
- # if we are able to use attach commands to create any kind of a target
- # and use it for debugging
- attachCommands = [
- 'target create -d "%s"' % (program),
- "process launch --stop-at-entry",
- ]
- initCommands = ["target list", "platform list"]
- preRunCommands = ["image list a.out", "image dump sections a.out"]
- postRunCommands = ["help trace", "help process trace"]
- stopCommands = ["frame variable", "thread backtrace"]
- exitCommands = ["expr 2+3", "expr 3+4"]
- terminateCommands = ["expr 4+2"]
- self.attach(
- program=program,
- attachCommands=attachCommands,
- initCommands=initCommands,
- preRunCommands=preRunCommands,
- stopCommands=stopCommands,
- exitCommands=exitCommands,
- terminateCommands=terminateCommands,
- postRunCommands=postRunCommands,
- )
- # Get output from the console. This should contain both the
- # "initCommands" and the "preRunCommands".
- output = self.get_console()
- # Verify all "initCommands" were found in console output
- self.verify_commands("initCommands", output, initCommands)
- # Verify all "preRunCommands" were found in console output
- self.verify_commands("preRunCommands", output, preRunCommands)
- # Verify all "postRunCommands" were found in console output
- self.verify_commands("postRunCommands", output, postRunCommands)
-
- functions = ["main"]
- 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=10, pattern=stopCommands[-1])
- self.verify_commands("stopCommands", output, stopCommands)
-
- # Continue after launch and hit the "pause()" call and stop the target.
- # Get output from the console. This should contain both the
- # "stopCommands" that were run after we stop.
- self.do_continue()
- time.sleep(0.5)
- self.dap_server.request_pause()
- self.dap_server.wait_for_stopped()
- output = self.collect_console(timeout=10, pattern=stopCommands[-1])
- 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
- # and the "terminateCommands" due to the debugging session ending
- output = self.collect_console(
- timeout=10.0,
- pattern=terminateCommands[0],
- )
- self.verify_commands("exitCommands", output, exitCommands)
- self.verify_commands("terminateCommands", output, terminateCommands)
-
- def test_attach_command_process_failures(self):
- """
- Tests that a 'attachCommands' is expected to leave the debugger's
- selected target with a valid process.
- """
- program = self.build_and_create_debug_adapter_for_attach()
- attachCommands = ['script print("oops, forgot to attach to a process...")']
- resp = self.attach(
- program=program,
- attachCommands=attachCommands,
- expectFailure=True,
- )
- self.assertFalse(resp["success"])
- self.assertIn(
- "attachCommands failed to attach to a process",
- resp["body"]["error"]["format"],
- )
-
- @skipIfNetBSD # Hangs on NetBSD as well
- def test_terminate_commands(self):
- """
- Tests that the "terminateCommands", that can be passed during
- attach, are run when the debugger is disconnected.
- """
- program = self.build_and_create_debug_adapter_for_attach()
-
- # Here we just create a target and launch the process as a way to test
- # if we are able to use attach commands to create any kind of a target
- # and use it for debugging
- attachCommands = [
- 'target create -d "%s"' % (program),
- "process launch --stop-at-entry",
- ]
- terminateCommands = ["expr 4+2"]
- self.attach(
- program=program,
- attachCommands=attachCommands,
- terminateCommands=terminateCommands,
- disconnectAutomatically=False,
- )
- self.get_console()
- # Once it's disconnected the console should contain the
- # "terminateCommands"
- self.dap_server.request_disconnect(terminateDebuggee=True)
- output = self.collect_console(
- timeout=1.0,
- pattern=terminateCommands[0],
- )
- self.verify_commands("terminateCommands", output, terminateCommands)
+ self.continue_and_verify_pid()
diff --git a/lldb/test/API/tools/lldb-dap/attach/main.c b/lldb/test/API/tools/lldb-dap/attach/main.c
index f56d5d53afa0..e14cf71a7044 100644
--- a/lldb/test/API/tools/lldb-dap/attach/main.c
+++ b/lldb/test/API/tools/lldb-dap/attach/main.c
@@ -2,7 +2,6 @@
#include <stdio.h>
#ifdef _WIN32
#include <process.h>
-#include <windows.h>
#else
#include <unistd.h>
#endif
@@ -20,11 +19,9 @@ int main(int argc, char const *argv[]) {
fclose(f);
}
+ // Wait on input from stdin.
+ getchar();
+
printf("pid = %i\n", getpid());
-#ifdef _WIN32
- Sleep(10 * 1000);
-#else
- sleep(10);
-#endif
- return 0; // breakpoint 1
+ return 0;
}
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 151ad761a504..beab4d6c1f5a 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
@@ -82,14 +82,14 @@ class TestDAP_breakpointEvents(lldbdap_testcase.DAPTestCaseBase):
)
# Flush the breakpoint events.
- self.dap_server.wait_for_breakpoint_events(timeout=5)
+ self.dap_server.wait_for_breakpoint_events()
# Continue to the breakpoint
self.continue_to_breakpoints(dap_breakpoint_ids)
verified_breakpoint_ids = []
unverified_breakpoint_ids = []
- for breakpoint_event in self.dap_server.wait_for_breakpoint_events(timeout=5):
+ for breakpoint_event in self.dap_server.wait_for_breakpoint_events():
breakpoint = breakpoint_event["body"]["breakpoint"]
id = breakpoint["id"]
if breakpoint["verified"]:
diff --git a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
index e722fcea9283..14789a669468 100644
--- a/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
+++ b/lldb/test/API/tools/lldb-dap/cancel/TestDAP_cancel.py
@@ -46,7 +46,7 @@ class TestDAP_cancel(lldbdap_testcase.DAPTestCaseBase):
# Use a relatively short timeout since this is only to ensure the
# following request is queued.
- blocking_seq = self.async_blocking_request(duration=1.0)
+ blocking_seq = self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 10)
# Use a longer timeout to ensure we catch if the request was interrupted
# properly.
pending_seq = self.async_blocking_request(duration=self.DEFAULT_TIMEOUT / 2)
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 e61d2480ea4b..f53813a8a48f 100644
--- a/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
+++ b/lldb/test/API/tools/lldb-dap/commands/TestDAP_commands.py
@@ -23,7 +23,6 @@ class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase):
exitCommands=["?" + command_quiet, command_not_quiet],
)
full_output = self.collect_console(
- timeout=1.0,
pattern=command_not_quiet,
)
self.assertNotIn(command_quiet, full_output)
@@ -51,7 +50,6 @@ class TestDAP_commands(lldbdap_testcase.DAPTestCaseBase):
expectFailure=True,
)
full_output = self.collect_console(
- timeout=1.0,
pattern=command_abort_on_error,
)
self.assertNotIn(command_quiet, full_output)
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 af5c62a8c4eb..9fbe9aaee8c6 100644
--- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
+++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
@@ -44,7 +44,7 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
"""
process = self.launch()
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), EXIT_SUCCESS)
+ self.assertEqual(process.wait(timeout=self.DEFAULT_TIMEOUT), EXIT_SUCCESS)
def test_invalid_header(self):
"""
@@ -54,7 +54,7 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
process = self.launch()
process.stdin.write(b"not the correct message header")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
+ self.assertEqual(process.wait(timeout=self.DEFAULT_TIMEOUT), EXIT_FAILURE)
def test_partial_header(self):
"""
@@ -64,7 +64,7 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
process = self.launch()
process.stdin.write(b"Content-Length: ")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
+ self.assertEqual(process.wait(timeout=self.DEFAULT_TIMEOUT), EXIT_FAILURE)
def test_incorrect_content_length(self):
"""
@@ -74,7 +74,7 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
process = self.launch()
process.stdin.write(b"Content-Length: abc")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
+ self.assertEqual(process.wait(timeout=self.DEFAULT_TIMEOUT), EXIT_FAILURE)
def test_partial_content_length(self):
"""
@@ -84,4 +84,4 @@ class TestDAP_io(lldbdap_testcase.DAPTestCaseBase):
process = self.launch()
process.stdin.write(b"Content-Length: 10\r\n\r\n{")
process.stdin.close()
- self.assertEqual(process.wait(timeout=5.0), EXIT_FAILURE)
+ self.assertEqual(process.wait(timeout=self.DEFAULT_TIMEOUT), 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 ceef95dfcd0d..8db2316e73fc 100644
--- a/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
+++ b/lldb/test/API/tools/lldb-dap/launch/TestDAP_launch.py
@@ -632,7 +632,27 @@ class TestDAP_launch(lldbdap_testcase.DAPTestCaseBase):
program = self.getBuildArtifact("a.out")
with tempfile.NamedTemporaryFile("rt") as f:
- self.launch(program, stdio=[None, f.name, None])
+ self.launch(program, stdio=[None, f.name])
+ self.continue_to_exit()
+ lines = f.readlines()
+ self.assertIn(
+ program, lines[0], "make sure program path is in first argument"
+ )
+
+ @skipIfAsan
+ @skipIfWindows
+ @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
+ def test_stdio_redirection_and_console(self):
+ """
+ Test stdio redirection and console.
+ """
+ self.build_and_create_debug_adapter()
+ program = self.getBuildArtifact("a.out")
+
+ with tempfile.NamedTemporaryFile("rt") as f:
+ self.launch(
+ program, console="integratedTerminal", stdio=[None, f.name, None]
+ )
self.continue_to_exit()
lines = f.readlines()
self.assertIn(
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 bb835af12f5e..1f4afabbd161 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"])
while event is not None:
- event = self.dap_server.wait_for_event(["module"], 0.25)
+ event = self.dap_server.wait_for_event(["module"])
# 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"])
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"])
self.assertIsNotNone(event, "didn't get a module event")
reason = event["body"]["reason"]
self.assertEqual(reason, "removed")
@@ -55,8 +55,4 @@ class TestDAP_module_event(lldbdap_testcase.DAPTestCaseBase):
self.assertListEqual(list(module_data.keys()), required_keys)
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)
- 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 c5a68372d822..0ed53dac5d86 100644
--- a/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
+++ b/lldb/test/API/tools/lldb-dap/module/TestDAP_module.py
@@ -67,7 +67,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"])
while module_event is not None:
reason = module_event["body"]["reason"]
if reason == "new":
@@ -75,7 +75,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"])
# 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/output/TestDAP_output.py b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
index fe978a9a7335..0065258920ec 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=1.0, pattern="abcdef")
+ output = self.collect_stdout(pattern="abcdef")
self.assertTrue(output and len(output) > 0, "expect program stdout")
self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_console.py b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_console.py
index 67483798f226..e1ad1425a993 100644
--- a/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_console.py
+++ b/lldb/test/API/tools/lldb-dap/restart/TestDAP_restart_console.py
@@ -105,7 +105,7 @@ class TestDAP_restart_console(lldbdap_testcase.DAPTestCaseBase):
# Restart and check that we still get a stopped event before reaching
# main.
self.dap_server.request_restart()
- stopped_events = self.dap_server.wait_for_stopped(timeout=20)
+ stopped_events = self.dap_server.wait_for_stopped()
self.verify_stopped_on_entry(stopped_events)
# continue to main
diff --git a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
index 08c225b3cada..6008a0cb0237 100644
--- a/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
+++ b/lldb/test/API/tools/lldb-dap/stackTraceDisassemblyDisplay/TestDAP_stackTraceDisassemblyDisplay.py
@@ -29,7 +29,7 @@ class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
"""
Build the program and run until the breakpoint is hit, and return the stack frames.
"""
- other_source_file = "other.c"
+ other_source_file = self.getBuildArtifact("other.c")
with delete_file_on_exit(other_source_file):
with open(other_source_file, "w") as f:
f.write(OTHER_C_SOURCE_CODE)
@@ -169,3 +169,4 @@ class TestDAP_stackTraceMissingSourcePath(lldbdap_testcase.DAPTestCaseBase):
self.verify_frames_source(
frames, main_frame_assembly=False, other_frame_assembly=False
)
+ self.continue_to_exit()
diff --git a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
index 13a694602f23..977d6ce9dac8 100644
--- a/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
+++ b/lldb/test/API/tools/lldb-dap/variables/TestDAP_variables.py
@@ -65,6 +65,11 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
self.assertNotIn(
key, actual, 'key "%s" is not expected in %s' % (key, actual)
)
+ isReadOnly = verify_dict.get("readOnly", False)
+ attributes = actual.get("presentationHint", {}).get("attributes", [])
+ self.assertEqual(
+ isReadOnly, "readOnly" in attributes, "%s %s" % (verify_dict, actual)
+ )
hasVariablesReference = "variablesReference" in actual
varRef = None
if hasVariablesReference:
@@ -179,8 +184,9 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
"children": {
"x": {"equals": {"type": "int", "value": "11"}},
"y": {"equals": {"type": "int", "value": "22"}},
- "buffer": {"children": buffer_children},
+ "buffer": {"children": buffer_children, "readOnly": True},
},
+ "readOnly": True,
},
"x": {"equals": {"type": "int"}},
}
@@ -444,8 +450,10 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
"buffer": {
"children": buffer_children,
"equals": {"indexedVariables": 16},
+ "readOnly": True,
},
},
+ "readOnly": True,
},
"x": {
"equals": {"type": "int"},
@@ -528,7 +536,7 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
"children": {
"x": {"equals": {"type": "int", "value": "11"}},
"y": {"equals": {"type": "int", "value": "22"}},
- "buffer": {"children": buffer_children},
+ "buffer": {"children": buffer_children, "readOnly": True},
},
}
@@ -622,11 +630,17 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
# "[raw]" child.
raw_child_count = 1 if enableSyntheticChildDebugging else 0
verify_locals = {
- "small_array": {"equals": {"indexedVariables": 5}},
- "large_array": {"equals": {"indexedVariables": 200}},
- "small_vector": {"equals": {"indexedVariables": 5 + raw_child_count}},
- "large_vector": {"equals": {"indexedVariables": 200 + raw_child_count}},
- "pt": {"missing": ["indexedVariables"]},
+ "small_array": {"equals": {"indexedVariables": 5}, "readOnly": True},
+ "large_array": {"equals": {"indexedVariables": 200}, "readOnly": True},
+ "small_vector": {
+ "equals": {"indexedVariables": 5 + raw_child_count},
+ "readOnly": True,
+ },
+ "large_vector": {
+ "equals": {"indexedVariables": 200 + raw_child_count},
+ "readOnly": True,
+ },
+ "pt": {"missing": ["indexedVariables"], "readOnly": True},
}
self.verify_variables(verify_locals, locals)
@@ -640,7 +654,10 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
"[4]": {"equals": {"type": "int", "value": "0"}},
}
if enableSyntheticChildDebugging:
- verify_children["[raw]"] = ({"contains": {"type": ["vector"]}},)
+ verify_children["[raw]"] = {
+ "contains": {"type": ["vector"]},
+ "readOnly": True,
+ }
children = self.dap_server.request_variables(locals[2]["variablesReference"])[
"body"
@@ -660,7 +677,7 @@ class TestDAP_variables(lldbdap_testcase.DAPTestCaseBase):
return_name: {"equals": {"type": "int", "value": "300"}},
"argc": {},
"argv": {},
- "pt": {},
+ "pt": {"readOnly": True},
"x": {},
"return_result": {"equals": {"type": "int"}},
}
diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py
index 13c4a85f51f7..ef1d546cf171 100644
--- a/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py
+++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteFork.py
@@ -2,6 +2,7 @@ import random
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
+from lldbsuite.support import seven
from fork_testbase import GdbRemoteForkTestBase
diff --git a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
index c01f6d83fafe..f1c0519ae56d 100644
--- a/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
+++ b/lldb/test/API/tools/lldb-server/TestLldbGdbServer.py
@@ -22,7 +22,9 @@ from lldbsuite.test.lldbtest import *
from lldbsuite.test.lldbdwarf import *
from lldbsuite.test import lldbutil, lldbplatformutil
-
+# On Linux systems with Yama ptrace_scope = 1 there is a race condition when the
+# debugee enables tracing. See https://github.com/llvm/llvm-project/issues/161510.
+@skipIfLinux
class LldbGdbServerTestCase(
gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcodeParser
):
diff --git a/lldb/test/API/tools/lldb-server/main.cpp b/lldb/test/API/tools/lldb-server/main.cpp
index 0e9323cce88c..7e84552b0f25 100644
--- a/lldb/test/API/tools/lldb-server/main.cpp
+++ b/lldb/test/API/tools/lldb-server/main.cpp
@@ -5,6 +5,7 @@
#include <cstdlib>
#include <cstring>
#include <errno.h>
+#include <fstream>
#include <future>
#include <inttypes.h>
#include <memory>
@@ -265,7 +266,11 @@ int main(int argc, char **argv) {
// Process command line args.
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
- if (consume_front(arg, "stderr:")) {
+ if (consume_front(arg, "syncfile:")) {
+ // Write to this file to tell test framework that attaching is now
+ // possible.
+ std::ofstream(arg).close();
+ } else if (consume_front(arg, "stderr:")) {
// Treat remainder as text to go to stderr.
fprintf(stderr, "%s\n", arg.c_str());
} else if (consume_front(arg, "retval:")) {
diff --git a/lldb/test/Shell/Driver/LocalLLDBInit.test b/lldb/test/Shell/Driver/LocalLLDBInit.test
index 5db545e7ec56..2aa8c527929f 100644
--- a/lldb/test/Shell/Driver/LocalLLDBInit.test
+++ b/lldb/test/Shell/Driver/LocalLLDBInit.test
@@ -9,7 +9,7 @@
# RUN: env HOME=%t.home %lldb-init -local-lldbinit -o 'settings show frame-format' 2>&1 | FileCheck %s --check-prefix=ALLOWINIT --check-prefix=NOINIT
# RUN: %lldb -o 'settings show frame-format' 2>&1 | FileCheck %s --check-prefix=NOINIT --check-prefix=CHECK
-# WARNINIT: There is a .lldbinit file in the current directory which is not being read.
+# WARNINIT: warning: There is a .lldbinit file in the current directory which is not being read.
# NOINIT-NOT: There is a .lldbinit file in the current directory which is not being read.
# CHECK-NOT: bogus
# ALLOWINIT: name 'prlnt' is not defined
diff --git a/lldb/test/Shell/Expr/TestExprLanguageNote.test b/lldb/test/Shell/Expr/TestExprLanguageNote.test
new file mode 100644
index 000000000000..e8e4e1399e45
--- /dev/null
+++ b/lldb/test/Shell/Expr/TestExprLanguageNote.test
@@ -0,0 +1,88 @@
+# RUN: split-file %s %t
+# RUN: %clang_host -g %t/main.cpp -o %t.out
+#
+# RUN: %lldb -x -b -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN: -s %t/no-target.input 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TARGET
+#
+# RUN: %lldb %t.out -x -b -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN: -s %t/with-target.input 2>&1 | FileCheck %s --check-prefix=CHECK-TARGET
+
+#--- main.cpp
+
+int main() {
+ int x = 10;
+ return x;
+}
+
+#--- with-target.input
+
+expr blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Falling back to default language. Ran expression as 'Objective C++'.
+
+b 4
+run
+
+expr blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Ran expression as '{{(ISO )?}}C++{{.*}}'
+
+expr -l objc -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Expression evaluation in pure Objective-C not supported. Ran expression as 'Objective C++'.
+
+expr -l c -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Expression evaluation in pure C not supported. Ran expression as 'ISO C++'.
+
+expr -l c++14 -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Ran expression as 'C++14'
+
+expr -l c++20 -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Ran expression as 'C++20'
+
+expr -l objective-c++ -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Ran expression as 'Objective C++'
+
+# D uses TypeSystemClang but running expressions in it isn't supported. Test that we warn about this.
+expr -l D -- blah
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET: note: Expression evaluation in D not supported. Falling back to default language. Ran expression as 'Objective C++'.
+
+expr -l c++17 -- x = 5
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET-NOT: note:
+
+expr x = 5
+
+# CHECK-TARGET: (lldb) expr
+# CHECK-TARGET-NOT: note:
+
+#--- no-target.input
+
+expr blah
+
+# CHECK-NO-TARGET: (lldb) expr
+# CHECK-NO-TARGET: note: Falling back to default language. Ran expression as 'Objective C++'.
+
+expr -l c++ -- 1 + 1
+
+# CHECK-NO-TARGET: (lldb) expr
+# CHECK-NO-TARGET-NOT: note:
+
+expr 1 + 1
+
+# CHECK-NO-TARGET: (lldb) expr
+# CHECK-NO-TARGET-NOT: note:
diff --git a/lldb/test/Shell/Expr/TestGlobalSymbolObjCConflict.c b/lldb/test/Shell/Expr/TestGlobalSymbolObjCConflict.c
new file mode 100644
index 000000000000..8f1bb62874a1
--- /dev/null
+++ b/lldb/test/Shell/Expr/TestGlobalSymbolObjCConflict.c
@@ -0,0 +1,35 @@
+// XFAIL: target-windows
+
+// Tests that LLDB correctly parses global symbols
+// starting with 'O'. On some platforms (e.g., Darwin)
+// C-symbols are prefixed with a '_'. The LLDB Macho-O
+// parses handles Objective-C metadata symbols starting
+// with '_OBJC' specially. This test ensures that we don't
+// lose track of regular global symbols with a '_O' prefix
+// in this.
+
+// RUN: %clang_host -c -g -fno-common %s -o %t.o
+// RUN: %clang_host %t.o -o %t.out
+// RUN: %lldb -b -x %t.out \
+// RUN: -o "b 29" \
+// RUN: -o "run" \
+// RUN: -o "p OglobalVar" \
+// RUN: -o "p Oabc" | FileCheck %s
+
+typedef struct {
+ int a;
+} Oabc_t;
+
+Oabc_t Oabc;
+int OglobalVar;
+
+int main(int argc, const char *argv[]) {
+ Oabc.a = 15;
+ OglobalVar = 10;
+ return OglobalVar + Oabc.a;
+}
+
+// CHECK: (lldb) p OglobalVar
+// CHECK: (int) 10
+// CHECK: (lldb) p Oabc
+// CHECK: (Oabc_t) (a = 15)
diff --git a/lldb/test/Shell/SymbolFile/DWARF/incomplete-member-beyond-parent-bounds.yaml b/lldb/test/Shell/SymbolFile/DWARF/incomplete-member-beyond-parent-bounds.yaml
new file mode 100644
index 000000000000..4e659d02b3cd
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/incomplete-member-beyond-parent-bounds.yaml
@@ -0,0 +1,104 @@
+# This is DWARF where we placed an incomplete type
+# at an offset that is the parent DW_AT_byte_size. Check
+# that we don't report an error in such cases.
+#
+# DW_TAG_compile_unit
+# DW_AT_name ("main.cpp")
+# DW_AT_language (DW_LANG_C)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Incomplete")
+# DW_AT_external (true)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Foo")
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_member
+# DW_AT_name ("mem")
+# DW_AT_data_member_location ("0x04")
+# DW_AT_type (0x00000011 "Incomplete")
+#
+# NULL
+#
+# NULL
+
+# RUN: yaml2obj %s > %t
+# RUN: lldb-test symbols --name=Foo --find=type %t 2>&1 | FileCheck %s
+
+# CHECK: Found 1 types:
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - main.cpp
+ - Incomplete
+ - Foo
+ - mem
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_udata
+ - Code: 0x2
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_external
+ Form: DW_FORM_flag_present
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x4
+ Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_data_member_location
+ Form: DW_FORM_data1
+ debug_info:
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0
+ - Value: 0x2
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x9
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x14
+ - Value: 0x04
+ - AbbrCode: 0x4
+ Values:
+ - Value: 0x18
+ - Value: 0x11
+ - Value: 0x04
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
diff --git a/lldb/test/Shell/SymbolFile/DWARF/member-beyond-parent-bounds.yaml b/lldb/test/Shell/SymbolFile/DWARF/member-beyond-parent-bounds.yaml
new file mode 100644
index 000000000000..2ac538ed1a85
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/member-beyond-parent-bounds.yaml
@@ -0,0 +1,109 @@
+# This is malformed DWARF where we placed a non-zero sized type
+# at an offset that is larger the parent DW_AT_byte_size. Check
+# that we report an error in such cases.
+#
+# DW_TAG_compile_unit
+# DW_AT_name ("main.cpp")
+# DW_AT_language (DW_LANG_C)
+#
+# DW_TAG_base_type
+# DW_AT_name ("int")
+# DW_AT_encoding (DW_ATE_signed)
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Foo")
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_member
+# DW_AT_name ("mem")
+# DW_AT_data_member_location ("0x05")
+# DW_AT_type (0x00000011 "int")
+#
+# NULL
+#
+# NULL
+
+# RUN: yaml2obj %s > %t
+# RUN: lldb-test symbols --name=Foo --find=type %t 2>&1 | FileCheck %s
+
+# CHECK: Found 1 types:
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - main.cpp
+ - int
+ - Foo
+ - mem
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_udata
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x4
+ Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_data_member_location
+ Form: DW_FORM_data1
+ debug_info:
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0
+ - Value: 0x2
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x9
+ - Value: 0x5
+ - Value: 0x4
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x0d
+ - Value: 0x04
+ - AbbrCode: 0x4
+ Values:
+ - Value: 0x11
+ - Value: 0x11
+ - Value: 0x05
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
diff --git a/lldb/test/Shell/SymbolFile/DWARF/member-on-parent-bounds.yaml b/lldb/test/Shell/SymbolFile/DWARF/member-on-parent-bounds.yaml
new file mode 100644
index 000000000000..736697c002ee
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/member-on-parent-bounds.yaml
@@ -0,0 +1,109 @@
+# This is malformed DWARF where we placed a non-zero sized type
+# at an offset that is the parent DW_AT_byte_size. Check
+# that we report an error in such cases.
+#
+# DW_TAG_compile_unit
+# DW_AT_name ("main.cpp")
+# DW_AT_language (DW_LANG_C)
+#
+# DW_TAG_base_type
+# DW_AT_name ("int")
+# DW_AT_encoding (DW_ATE_signed)
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Foo")
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_member
+# DW_AT_name ("mem")
+# DW_AT_data_member_location ("0x04")
+# DW_AT_type (0x00000011 "int")
+#
+# NULL
+#
+# NULL
+
+# RUN: yaml2obj %s > %t
+# RUN: lldb-test symbols --name=Foo --find=type %t 2>&1 | FileCheck %s
+
+# CHECK: Found 1 types:
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - main.cpp
+ - int
+ - Foo
+ - mem
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_udata
+ - Code: 0x2
+ Tag: DW_TAG_base_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_encoding
+ Form: DW_FORM_data1
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x4
+ Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_data_member_location
+ Form: DW_FORM_data1
+ debug_info:
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0
+ - Value: 0x2
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x9
+ - Value: 0x5
+ - Value: 0x4
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x0d
+ - Value: 0x04
+ - AbbrCode: 0x4
+ Values:
+ - Value: 0x11
+ - Value: 0x11
+ - Value: 0x04
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
diff --git a/lldb/test/Shell/SymbolFile/DWARF/union-types-no-member-location.yaml b/lldb/test/Shell/SymbolFile/DWARF/union-types-no-member-location.yaml
index fbdc626ed113..1d1e129cdb7c 100644
--- a/lldb/test/Shell/SymbolFile/DWARF/union-types-no-member-location.yaml
+++ b/lldb/test/Shell/SymbolFile/DWARF/union-types-no-member-location.yaml
@@ -48,14 +48,10 @@
# RUN: yaml2obj %s > %t
# RUN: lldb-test symbols --name=UnionType --find=type %t > %t.stdout
# RUN: cat %t.stdout | FileCheck --check-prefix=STDOUT %s
-# RUN: lldb-test symbols --name=UnionType --find=type %t 2> %t.stderr
-# RUN: cat %t.stderr | FileCheck --allow-empty --check-prefix=STDERR %s
# STDOUT: Found 1 types:
# STDOUT: {{(0x)?[0-9a-fA-F]+}}: Type{0x0000002b} , name = "UnionType", size = 32, compiler_type = 0x{{[0-9a-fA-F]+}} union UnionType {
-# STDERR-NOT: error: union-types-no-member-location.yaml.tmp 0x00000031: DW_TAG_member 'array' refers to type 0x000000000000001f which extends beyond the bounds of 0x0000002b
-
--- !ELF
FileHeader:
Class: ELFCLASS64
diff --git a/lldb/test/Shell/SymbolFile/DWARF/zero-sized-member-in-parent-bounds.yaml b/lldb/test/Shell/SymbolFile/DWARF/zero-sized-member-in-parent-bounds.yaml
new file mode 100644
index 000000000000..a98f62cd3405
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/zero-sized-member-in-parent-bounds.yaml
@@ -0,0 +1,105 @@
+# This is DWARF where we placed a zero-sized type
+# at an offset that is the parent DW_AT_byte_size. Check
+# that we don't report an error in such cases.
+#
+# DW_TAG_compile_unit
+# DW_AT_name ("main.cpp")
+# DW_AT_language (DW_LANG_C)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Bar")
+# DW_AT_byte_size (0x00)
+#
+# DW_TAG_structure_type
+# DW_AT_name ("Foo")
+# DW_AT_byte_size (0x04)
+#
+# DW_TAG_member
+# DW_AT_name ("mem")
+# DW_AT_data_member_location ("0x04")
+# DW_AT_type (0x00000011 "Bar")
+#
+# NULL
+#
+# NULL
+
+# RUN: yaml2obj %s > %t
+# RUN: lldb-test symbols --name=Foo --find=type %t 2>&1 | FileCheck %s
+
+# CHECK: Found 1 types:
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+DWARF:
+ debug_str:
+ - main.cpp
+ - Bar
+ - Foo
+ - mem
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_language
+ Form: DW_FORM_udata
+ - Code: 0x2
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x3
+ Tag: DW_TAG_structure_type
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_byte_size
+ Form: DW_FORM_data1
+ - Code: 0x4
+ Tag: DW_TAG_member
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_type
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_data_member_location
+ Form: DW_FORM_data1
+ debug_info:
+ - Version: 4
+ AbbrevTableID: 0
+ AbbrOffset: 0x0
+ AddrSize: 8
+ Entries:
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x0
+ - Value: 0x2
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x9
+ - Value: 0x0
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x0d
+ - Value: 0x04
+ - AbbrCode: 0x4
+ Values:
+ - Value: 0x11
+ - Value: 0x11
+ - Value: 0x04
+ - AbbrCode: 0x0
+ - AbbrCode: 0x0
+...
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/break-by-function.cpp b/lldb/test/Shell/SymbolFile/NativePDB/break-by-function.cpp
index a580d574a9ca..d4499373bb86 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/break-by-function.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/break-by-function.cpp
@@ -50,9 +50,9 @@ int main(int argc, char **argv) {
// CHECK: 1: name = 'main', locations = 1
// CHECK: 1.1: where = break-by-function.cpp.tmp.exe`main + {{[0-9]+}}
// CHECK: 2: name = 'OvlGlobalFn', locations = 3
-// CHECK: 2.1: where = break-by-function.cpp.tmp.exe`OvlGlobalFn + {{[0-9]+}}
-// CHECK: 2.2: where = break-by-function.cpp.tmp.exe`OvlGlobalFn
-// CHECK: 2.3: where = break-by-function.cpp.tmp.exe`OvlGlobalFn + {{[0-9]+}}
+// CHECK: 2.1: where = break-by-function.cpp.tmp.exe`int OvlGlobalFn(int) + {{[0-9]+}}
+// CHECK: 2.2: where = break-by-function.cpp.tmp.exe`int OvlGlobalFn(int, int)
+// CHECK: 2.3: where = break-by-function.cpp.tmp.exe`int OvlGlobalFn(int, int, int) + {{[0-9]+}}
// CHECK: 3: name = 'StaticFn', locations = 1
// CHECK: 3.1: where = break-by-function.cpp.tmp.exe`StaticFn + {{[0-9]+}}
// CHECK: 4: name = 'DoesntExist', locations = 0 (pending)
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/break-by-line.cpp b/lldb/test/Shell/SymbolFile/NativePDB/break-by-line.cpp
index 90ac633b0163..3d7de3275ed6 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/break-by-line.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/break-by-line.cpp
@@ -24,4 +24,4 @@ int main(int argc, char **argv) {
// CHECK: (lldb) target create "{{.*}}break-by-line.cpp.tmp.exe"
// CHECK: Current executable set to '{{.*}}break-by-line.cpp.tmp.exe'
// CHECK: (lldb) break set -f break-by-line.cpp -l 15
-// CHECK: Breakpoint 1: where = break-by-line.cpp.tmp.exe`NS::NamespaceFn + {{[0-9]+}} at break-by-line.cpp:15
+// CHECK: Breakpoint 1: where = break-by-line.cpp.tmp.exe`int NS::NamespaceFn(int) + {{[0-9]+}} at break-by-line.cpp:15
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/c-calling-conventions.cpp b/lldb/test/Shell/SymbolFile/NativePDB/c-calling-conventions.cpp
new file mode 100644
index 000000000000..d1d0bb08dfec
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/c-calling-conventions.cpp
@@ -0,0 +1,51 @@
+// clang-format off
+// REQUIRES: lld, (target-x86 || target-x86_64)
+
+// RUN: %build --compiler=clang-cl --arch=32 --nodefaultlib --output=%t-32.exe %s
+// RUN: lldb-test symbols %t-32.exe | FileCheck --check-prefixes CHECK-32,CHECK-BOTH %s
+// RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib --output=%t-64.exe %s
+// RUN: lldb-test symbols %t-64.exe | FileCheck --check-prefixes CHECK-64,CHECK-BOTH %s
+
+extern "C" {
+int FuncCCall() { return 0; }
+int __stdcall FuncStdCall() { return 0; }
+int __fastcall FuncFastCall() { return 0; }
+int __vectorcall FuncVectorCall() { return 0; }
+
+int __cdecl _underscoreCdecl() { return 0; }
+int __stdcall _underscoreStdcall() { return 0; }
+int __fastcall _underscoreFastcall() { return 0; }
+int __vectorcall _underscoreVectorcall() { return 0; }
+}
+
+int main() {
+ FuncCCall();
+ FuncStdCall();
+ FuncFastCall();
+ FuncVectorCall();
+ _underscoreCdecl();
+ _underscoreStdcall();
+ _underscoreFastcall();
+ _underscoreVectorcall();
+ return 0;
+}
+
+// CHECK-BOTH-DAG: Function{{.*}}, demangled = FuncCCall,
+// CHECK-BOTH-DAG: Function{{.*}}, demangled = FuncVectorCall@@0,
+// CHECK-BOTH-DAG: Function{{.*}}, demangled = _underscoreCdecl,
+// CHECK-BOTH-DAG: Function{{.*}}, demangled = _underscoreVectorcall@@0,
+// CHECK-BOTH-DAG: Function{{.*}}, demangled = main,
+
+// __stdcall and __fastcall aren't available on 64 bit
+
+// CHECK-32-DAG: Function{{.*}}, demangled = _FuncStdCall@0,
+// CHECK-64-DAG: Function{{.*}}, demangled = FuncStdCall,
+
+// CHECK-32-DAG: Function{{.*}}, demangled = @FuncFastCall@0,
+// CHECK-64-DAG: Function{{.*}}, demangled = FuncFastCall,
+
+// CHECK-32-DAG: Function{{.*}}, demangled = __underscoreStdcall@0,
+// CHECK-64-DAG: Function{{.*}}, demangled = _underscoreStdcall,
+
+// CHECK-32-DAG: Function{{.*}}, demangled = @_underscoreFastcall@0,
+// CHECK-64-DAG: Function{{.*}}, demangled = _underscoreFastcall,
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/disassembly.cpp b/lldb/test/Shell/SymbolFile/NativePDB/disassembly.cpp
index b3f7b098a95d..05074aa61d32 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/disassembly.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/disassembly.cpp
@@ -25,7 +25,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: disassembly.cpp.tmp.exe[{{.*}}] <+12>: mov qword ptr [rsp + 0x28], rdx
// CHECK-NEXT: disassembly.cpp.tmp.exe[{{.*}}] <+17>: mov dword ptr [rsp + 0x24], ecx
// CHECK: ** 15 foo();
-// CHECK: disassembly.cpp.tmp.exe[{{.*}}] <+21>: call {{.*}} ; foo at disassembly.cpp:12
+// CHECK: disassembly.cpp.tmp.exe[{{.*}}] <+21>: call {{.*}} ; int foo(void) at disassembly.cpp:12
// CHECK: ** 16 return 0;
// CHECK-NEXT: 17 }
// CHECK-NEXT: 18
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
index 3ef7a4c94c28..6204cbd34a58 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
@@ -148,11 +148,12 @@ int main(int argc, char **argv) {
// 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: Function: id = {{.*}}, name = "int Class::overloaded_method(bool)"
// 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: Function: id = {{.*}}, name = "char overloaded_method(void)"
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "char overloaded_method(int)"
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (void)"
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (int)"
@@ -160,6 +161,6 @@ int main(int argc, char **argv) {
// 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: Function: id = {{.*}}, name = "bool Class::overloaded_method(void)"
// 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/local-variables-registers.s b/lldb/test/Shell/SymbolFile/NativePDB/local-variables-registers.s
index fe2f397d60c0..b44b99a33626 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/local-variables-registers.s
+++ b/lldb/test/Shell/SymbolFile/NativePDB/local-variables-registers.s
@@ -578,12 +578,12 @@ main: # @main
# CHECK: (lldb) image lookup -a 0x14000104e -v
# CHECK: LineEntry: [0x000000014000104e-0x0000000140001050): C:\src\test\a.cpp:1004
# CHECK-NEXT: Symbol: id = {{.*}}, range = [0x0000000140001011-0x0000000140001050), name="main"
-# CHECK-NEXT: Variable: id = {{.*}}, name = "simple_type1", type = "int64_t", valid ranges = <block>, location = [0x000000014000104e, 0x000000014000104f) -> DW_OP_reg26 XMM9, DW_OP_piece 0x4, DW_OP_reg24 XMM7, DW_OP_piece 0x4
+# CHECK-NEXT: Variable: id = {{.*}}, name = "simple_type1", type = "long long", valid ranges = <block>, location = [0x000000014000104e, 0x000000014000104f) -> DW_OP_reg26 XMM9, DW_OP_piece 0x4, DW_OP_reg24 XMM7, DW_OP_piece 0x4
# CHECK-EMPTY:
# CHECK: (lldb) image lookup -a 0x14000104f -v
# CHECK: LineEntry: [0x000000014000104e-0x0000000140001050): C:\src\test\a.cpp:1004
# CHECK-NEXT: Symbol: id = {{.*}}, range = [0x0000000140001011-0x0000000140001050), name="main"
-# CHECK-NEXT: Variable: id = {{.*}}, name = "simple_type1", type = "int64_t", valid ranges = <block>, location = [0x000000014000104f, 0x0000000140001050) -> DW_OP_reg26 XMM9, DW_OP_piece 0x4, DW_OP_piece 0x4
+# CHECK-NEXT: Variable: id = {{.*}}, name = "simple_type1", type = "long long", valid ranges = <block>, location = [0x000000014000104f, 0x0000000140001050) -> DW_OP_reg26 XMM9, DW_OP_piece 0x4, DW_OP_piece 0x4
# CHECK-EMPTY:
.Ltmp26:
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp b/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp
index 44a8dc14c615..f44a5b9dd56e 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/local-variables.cpp
@@ -55,7 +55,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: (lldb) step
// CHECK-NEXT: Process {{.*}} stopped
// CHECK-NEXT: * thread #1, stop reason = step in
-// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
+// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`int Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
// CHECK-NEXT: 6
// CHECK-NEXT: 7
// CHECK-NEXT: 8 int Function(int Param1, char Param2) {
@@ -71,7 +71,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: (lldb) step
// CHECK-NEXT: Process {{.*}} stopped
// CHECK-NEXT: * thread #1, stop reason = step in
-// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
+// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`int Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
// CHECK-NEXT: 7
// CHECK-NEXT: 8 int Function(int Param1, char Param2) {
// CHECK-NEXT: 9 unsigned Local1 = Param1 + 1;
@@ -89,7 +89,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: (lldb) step
// CHECK-NEXT: Process {{.*}} stopped
// CHECK-NEXT: * thread #1, stop reason = step in
-// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
+// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`int Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
// CHECK-NEXT: 8 int Function(int Param1, char Param2) {
// CHECK-NEXT: 9 unsigned Local1 = Param1 + 1;
// CHECK-NEXT: 10 char Local2 = Param2 + 1;
@@ -109,7 +109,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: (lldb) step
// CHECK-NEXT: Process {{.*}} stopped
// CHECK-NEXT: * thread #1, stop reason = step in
-// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
+// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`int Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
// CHECK-NEXT: 9 unsigned Local1 = Param1 + 1;
// CHECK-NEXT: 10 char Local2 = Param2 + 1;
// CHECK-NEXT: 11 ++Local1;
@@ -129,7 +129,7 @@ int main(int argc, char **argv) {
// CHECK-NEXT: (lldb) step
// CHECK-NEXT: Process {{.*}} stopped
// CHECK-NEXT: * thread #1, stop reason = step in
-// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
+// CHECK-NEXT: frame #0: {{.*}} local-variables.cpp.tmp.exe`int Function(Param1=16, Param2='a') at local-variables.cpp:{{.*}}
// CHECK-NEXT: 10 char Local2 = Param2 + 1;
// CHECK-NEXT: 11 ++Local1;
// CHECK-NEXT: 12 ++Local2;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/simple-types.cpp b/lldb/test/Shell/SymbolFile/NativePDB/simple-types.cpp
new file mode 100644
index 000000000000..3781194e2e99
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/simple-types.cpp
@@ -0,0 +1,142 @@
+// REQUIRES: lld
+
+// Test that simple types can be found
+// RUN: %build --std=c++20 --nodefaultlib --compiler=clang-cl --arch=64 -o %t.exe -- %s
+// RUN: lldb-test symbols %t.exe | FileCheck %s
+// RUN: lldb-test symbols %t.exe | FileCheck --check-prefix=FUNC-PARAMS %s
+
+bool *PB;
+bool &RB = *PB;
+bool *&RPB = PB;
+const bool &CRB = RB;
+bool *const BC = 0;
+const bool *const CBC = 0;
+
+long AL[2];
+
+const volatile short CVS = 0;
+const short CS = 0;
+volatile short VS;
+
+struct ReturnedStruct1 {};
+struct ReturnedStruct2 {};
+
+struct MyStruct {
+ static ReturnedStruct1 static_fn(char *) { return {}; }
+ ReturnedStruct2 const_member_fn(char *) const { return {}; }
+ void volatile_member_fn() volatile {};
+ void member_fn() {};
+};
+
+void (*PF)(int, bool *, const float, double, ...);
+
+using Func = void(char16_t, MyStruct &);
+Func *PF2;
+
+using SomeTypedef = long;
+SomeTypedef ST;
+
+int main() {
+ bool b;
+ char c;
+ unsigned char uc;
+ char8_t c8;
+
+ short s;
+ unsigned short us;
+ wchar_t wc;
+ char16_t c16;
+
+ int i;
+ unsigned int ui;
+ long l;
+ unsigned long ul;
+ char32_t c32;
+
+ long long ll;
+ unsigned long long ull;
+
+ MyStruct my_struct;
+
+ _Float16 f16;
+
+ _Complex float cf;
+ _Complex double cd;
+
+ __int128 i128;
+ unsigned __int128 ui128;
+
+ decltype(nullptr) np;
+}
+
+// CHECK-DAG: Type{{.*}} , name = "decltype(nullptr)", compiler_type = 0x{{[0-9a-f]+}} nullptr_t
+
+// CHECK-DAG: Type{{.*}} , name = "bool", size = 1, compiler_type = 0x{{[0-9a-f]+}} _Bool
+// CHECK-DAG: Type{{.*}} , name = "char", size = 1, compiler_type = 0x{{[0-9a-f]+}} char
+// CHECK-DAG: Type{{.*}} , name = "unsigned char", size = 1, compiler_type = 0x{{[0-9a-f]+}} unsigned char
+// CHECK-DAG: Type{{.*}} , name = "char8_t", size = 1, compiler_type = 0x{{[0-9a-f]+}} char8_t
+
+// CHECK-DAG: Type{{.*}} , name = "short", size = 2, compiler_type = 0x{{[0-9a-f]+}} short
+// CHECK-DAG: Type{{.*}} , name = "const volatile short", size = 2, compiler_type = 0x{{[0-9a-f]+}} const volatile short
+// CHECK-DAG: Type{{.*}} , name = "const short", size = 2, compiler_type = 0x{{[0-9a-f]+}} const short
+// CHECK-DAG: Type{{.*}} , name = "volatile short", size = 2, compiler_type = 0x{{[0-9a-f]+}} volatile short
+
+// CHECK-DAG: Type{{.*}} , name = "unsigned short", size = 2, compiler_type = 0x{{[0-9a-f]+}} unsigned short
+// CHECK-DAG: Type{{.*}} , name = "wchar_t", size = 2, compiler_type = 0x{{[0-9a-f]+}} wchar_t
+// CHECK-DAG: Type{{.*}} , name = "char16_t", size = 2, compiler_type = 0x{{[0-9a-f]+}} char16_t
+
+// CHECK-DAG: Type{{.*}} , name = "int", size = 4, compiler_type = 0x{{[0-9a-f]+}} int
+// CHECK-DAG: Type{{.*}} , name = "unsigned", size = 4, compiler_type = 0x{{[0-9a-f]+}} unsigned int
+// CHECK-DAG: Type{{.*}} , name = "long", size = 4, compiler_type = 0x{{[0-9a-f]+}} long
+// CHECK-DAG: Type{{.*}} , name = "unsigned long", size = 4, compiler_type = 0x{{[0-9a-f]+}} unsigned long
+// CHECK-DAG: Type{{.*}} , name = "char32_t", size = 4, compiler_type = 0x{{[0-9a-f]+}} char32_t
+
+// CHECK-DAG: Type{{.*}} , name = "long long", size = 8, compiler_type = 0x{{[0-9a-f]+}} long long
+// CHECK-DAG: Type{{.*}} , name = "unsigned long long", size = 8, compiler_type = 0x{{[0-9a-f]+}} unsigned long long
+
+// CHECK-DAG: Type{{.*}} , name = "__int128", size = 16, compiler_type = 0x{{[0-9a-f]+}} __int128
+// CHECK-DAG: Type{{.*}} , name = "unsigned __int128", size = 16, compiler_type = 0x{{[0-9a-f]+}} unsigned __int128
+
+// CHECK-DAG: Type{{.*}} , name = "_Float16", size = 2, compiler_type = 0x{{[0-9a-f]+}} __fp16
+// CHECK-DAG: Type{{.*}} , name = "float", size = 4, compiler_type = 0x{{[0-9a-f]+}} float
+// CHECK-DAG: Type{{.*}} , name = "const float", size = 4, compiler_type = 0x{{[0-9a-f]+}} const float
+
+// CHECK-DAG: Type{{.*}} , name = "_Complex float", size = 4, compiler_type = 0x{{[0-9a-f]+}} _Complex float
+// CHECK-DAG: Type{{.*}} , name = "_Complex double", size = 8, compiler_type = 0x{{[0-9a-f]+}} _Complex double
+
+// CHECK-DAG: Type{{.*}} , name = "ReturnedStruct1", size = 1, decl = simple-types.cpp:21, compiler_type = 0x{{[0-9a-f]+}} struct ReturnedStruct1 {
+// CHECK-DAG: Type{{.*}} , name = "ReturnedStruct2", size = 1, decl = simple-types.cpp:22, compiler_type = 0x{{[0-9a-f]+}} struct ReturnedStruct2 {
+// CHECK-DAG: Type{{.*}} , name = "MyStruct", size = 1, decl = simple-types.cpp:24, compiler_type = 0x{{[0-9a-f]+}} struct MyStruct {
+
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} struct MyStruct *const
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} const struct MyStruct *const
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} volatile struct MyStruct *const
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} struct MyStruct &
+
+// CHECK-DAG: Type{{.*}} , name = "const bool", size = 1, compiler_type = 0x{{[0-9a-f]+}} const _Bool
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} _Bool &
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} _Bool *
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} _Bool *&
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} const _Bool &
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} _Bool *const
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} const _Bool *const
+
+// CHECK-DAG: Type{{.*}} , name = "SomeTypedef", size = 4, compiler_type = 0x{{[0-9a-f]+}} typedef SomeTypedef
+// CHECK-DAG: Type{{.*}} , name = "Func", size = 0, compiler_type = 0x{{[0-9a-f]+}} typedef Func
+
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} int (void)
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} void (void)
+
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} void (int, _Bool *, const float, double, ...)
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} void (*)(int, _Bool *, const float, double, ...)
+
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} void (char16_t, struct MyStruct &)
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} void (*)(char16_t, struct MyStruct &)
+
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} struct ReturnedStruct1 (char *)
+// CHECK-DAG: Type{{.*}} , size = 0, compiler_type = 0x{{[0-9a-f]+}} struct ReturnedStruct2 (char *)
+
+// CHECK-DAG: Type{{.*}} , size = 8, compiler_type = 0x{{[0-9a-f]+}} long[2]
+
+// double is used as a parameter to `PF`, but not created as an LLDB type
+// FUNC-PARAMS-NOT: Type{{.*}} , name = "double"
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp b/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp
index 596a826f4a11..87eeebe7aa1b 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/stack_unwinding01.cpp
@@ -24,19 +24,19 @@ int main(int argc, char **argv) {
// CHECK: (lldb) thread backtrace
// CHECK-NEXT: * thread #1, stop reason = breakpoint 1.1
-// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
// CHECK-NEXT: frame #1: {{.*}} stack_unwinding01.cpp.tmp.exe`main(argc={{.*}}, argv={{.*}}) at stack_unwinding01.cpp:20
// CHECK: (lldb) thread backtrace
// CHECK-NEXT: * thread #1, stop reason = breakpoint 1.1
-// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=3, b=2) at stack_unwinding01.cpp:12
-// CHECK-NEXT: frame #1: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=3, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: frame #1: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
// CHECK-NEXT: frame #2: {{.*}} stack_unwinding01.cpp.tmp.exe`main(argc={{.*}}, argv={{.*}}) at stack_unwinding01.cpp:20
// CHECK: (lldb) thread backtrace
// CHECK-NEXT: * thread #1, stop reason = breakpoint 1.1
-// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=4, b=2) at stack_unwinding01.cpp:12
-// CHECK-NEXT: frame #1: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=3, b=2) at stack_unwinding01.cpp:12
-// CHECK-NEXT: frame #2: {{.*}} stack_unwinding01.cpp.tmp.exe`Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: * frame #0: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=4, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: frame #1: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=3, b=2) at stack_unwinding01.cpp:12
+// CHECK-NEXT: frame #2: {{.*}} stack_unwinding01.cpp.tmp.exe`void Struct::simple_method(this={{.*}}, a=2, b=2) at stack_unwinding01.cpp:12
// CHECK-NEXT: frame #3: {{.*}} stack_unwinding01.cpp.tmp.exe`main(argc={{.*}}, argv={{.*}}) at stack_unwinding01.cpp:20
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp b/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
index 81d643d9572d..beb5ae2f9025 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/symtab.cpp
@@ -1,4 +1,4 @@
-// REQUIRES: x86
+// REQUIRES: lld, x86
// Test symtab reading
// RUN: %build --compiler=clang-cl --arch=64 --nodefaultlib -o %t.exe -- %s
diff --git a/lldb/test/Shell/SymbolFile/PDB/function-nested-block.test b/lldb/test/Shell/SymbolFile/PDB/function-nested-block.test
index 9057d01c2584..4a2355bf23c9 100644
--- a/lldb/test/Shell/SymbolFile/PDB/function-nested-block.test
+++ b/lldb/test/Shell/SymbolFile/PDB/function-nested-block.test
@@ -4,7 +4,7 @@ RUN: lldb-test symbols -find=function -file FunctionNestedBlockTest.cpp -line 4
RUN: lldb-test symbols -find=block -file FunctionNestedBlockTest.cpp -line 4 %t.exe | FileCheck --check-prefix=CHECK-BLOCK %s
CHECK-FUNCTION: Found 1 functions:
-CHECK-FUNCTION: name = "{{.*}}", mangled = "{{_?}}main"
+CHECK-FUNCTION: name = "main"
CHECK-BLOCK: Found 1 blocks:
CHECK-BLOCK: Blocks: id = {{.*}}, range = {{.*}}
diff --git a/lldb/test/Shell/SymbolFile/PDB/variables.test b/lldb/test/Shell/SymbolFile/PDB/variables.test
index 970d714c29c3..cb761de38241 100644
--- a/lldb/test/Shell/SymbolFile/PDB/variables.test
+++ b/lldb/test/Shell/SymbolFile/PDB/variables.test
@@ -42,7 +42,7 @@ GLOBALS-DAG: Variable{{.*}}, name = "g_Const"
GLOBALS-SAME: scope = ??? (2)
GLOBALS: Function
-FUNC-F: Function{{.*}}, {{mangled = \?f@@YAHHH@Z|demangled = f}}
+FUNC-F: Function{{.*}}, mangled = ?f@@YAHHH@Z
FUNC-F-NEXT: Block
FUNC-F-NEXT: Variable{{.*}}, name = "var_arg1"
FUNC-F-SAME: scope = parameter
@@ -64,14 +64,14 @@ FUNC-MAIN-SAME: scope = local
FUNC-MAIN-NEXT: Variable{{.*}}, name = "a"
FUNC-MAIN-SAME: scope = local
-FUNC-CONSTRUCTOR: Function{{.*}}, {{(de)?}}mangled = {{.*}}Class::Class{{.*}}
+FUNC-CONSTRUCTOR: Function{{.*}}, {{mangled = \?\?0Class@@QEAA@H@Z|demangled = .*Class::Class}}
FUNC-CONSTRUCTOR-NEXT: Block
FUNC-CONSTRUCTOR-NEXT: Variable{{.*}}, name = "this"
FUNC-CONSTRUCTOR-SAME: scope = parameter
FUNC-CONSTRUCTOR-NEXT: Variable{{.*}}, name = "a"
FUNC-CONSTRUCTOR-SAME: scope = parameter
-FUNC-MEMBER: Function{{.*}}, {{(de)?}}mangled = {{.*}}{{(Class::)?}}Func{{.*}}
+FUNC-MEMBER: Function{{.*}}, {{mangled = \?Func@Class@@QEAAXXZ|demangled = .*Class::Func}}
FUNC-MEMBER-NEXT: Block
FUNC-MEMBER-NEXT: Variable{{.*}}, name = "this"
FUNC-MEMBER-SAME: scope = parameter
diff --git a/lldb/test/Shell/helper/build.py b/lldb/test/Shell/helper/build.py
index caaa14f90af1..a5a7e997be04 100755
--- a/lldb/test/Shell/helper/build.py
+++ b/lldb/test/Shell/helper/build.py
@@ -9,12 +9,8 @@ import subprocess
import sys
if sys.platform == "win32":
- # This module was renamed in Python 3. Make sure to import it using a
- # consistent name regardless of python version.
- try:
- import winreg
- except:
- import _winreg as winreg
+ import winreg
+
if __name__ != "__main__":
raise RuntimeError("Do not import this script, run it instead")
diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py
index 505847fb763e..cdc0cfe51f7c 100644
--- a/lldb/test/Shell/lit.cfg.py
+++ b/lldb/test/Shell/lit.cfg.py
@@ -33,7 +33,7 @@ config.test_format = toolchain.ShTestLldb(not use_lit_shell)
# suffixes: A list of file extensions to treat as test files. This is overriden
# by individual lit.local.cfg files in the test subdirectories.
-config.suffixes = [".test", ".cpp", ".s", ".m", ".ll"]
+config.suffixes = [".test", ".cpp", ".s", ".m", ".ll", ".c"]
# excludes: A list of directories to exclude from the testsuite. The 'Inputs'
# subdirectories contain auxiliary inputs for various tests in their parent
diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp
index f541134b43a1..0cd48d91a682 100644
--- a/lldb/tools/debugserver/source/DNB.cpp
+++ b/lldb/tools/debugserver/source/DNB.cpp
@@ -1386,6 +1386,16 @@ int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
return -1;
}
+nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ MachProcessSP procSP;
+ if (GetProcessSP(pid, procSP))
+ return procSP->Task().GetMemoryTags(addr, size, tags);
+
+ return false;
+}
+
std::string DNBProcessGetProfileData(nub_process_t pid,
DNBProfileDataScanType scanType) {
MachProcessSP procSP;
diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h
index 10d1f6879435..1f3d5392c588 100644
--- a/lldb/tools/debugserver/source/DNB.h
+++ b/lldb/tools/debugserver/source/DNB.h
@@ -105,6 +105,9 @@ nub_bool_t DNBProcessMemoryDeallocate(nub_process_t pid,
nub_addr_t addr) DNB_EXPORT;
int DNBProcessMemoryRegionInfo(nub_process_t pid, nub_addr_t addr,
DNBRegionInfo *region_info) DNB_EXPORT;
+nub_bool_t DNBProcessGetMemoryTags(nub_process_t pid, nub_addr_t addr,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) DNB_EXPORT;
std::string
DNBProcessGetProfileData(nub_process_t pid,
DNBProfileDataScanType scanType) DNB_EXPORT;
diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h
index df8ca809d412..d98399aed5e1 100644
--- a/lldb/tools/debugserver/source/DNBDefs.h
+++ b/lldb/tools/debugserver/source/DNBDefs.h
@@ -358,10 +358,11 @@ struct DNBExecutableImageInfo {
struct DNBRegionInfo {
public:
DNBRegionInfo()
- : addr(0), size(0), permissions(0), dirty_pages(), vm_types() {}
+ : addr(0), size(0), permissions(0), flags(), dirty_pages(), vm_types() {}
nub_addr_t addr;
nub_addr_t size;
uint32_t permissions;
+ std::vector<std::string> flags;
std::vector<nub_addr_t> dirty_pages;
std::vector<std::string> vm_types;
};
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.h b/lldb/tools/debugserver/source/MacOSX/MachTask.h
index 2284f6b99de9..c4a20b80fda9 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachTask.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.h
@@ -56,6 +56,8 @@ public:
nub_size_t ReadMemory(nub_addr_t addr, nub_size_t size, void *buf);
nub_size_t WriteMemory(nub_addr_t addr, nub_size_t size, const void *buf);
int GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info);
+ nub_bool_t GetMemoryTags(nub_addr_t addr, nub_size_t size,
+ std::vector<uint8_t> &tags);
std::string GetProfileData(DNBProfileDataScanType scanType);
nub_addr_t AllocateMemory(nub_size_t size, uint32_t permissions);
diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.mm b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
index 8ae9d4df9965..21156feecba2 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachTask.mm
+++ b/lldb/tools/debugserver/source/MacOSX/MachTask.mm
@@ -213,7 +213,7 @@ nub_size_t MachTask::WriteMemory(nub_addr_t addr, nub_size_t size,
}
//----------------------------------------------------------------------
-// MachTask::MemoryRegionInfo
+// MachTask::GetMemoryRegionInfo
//----------------------------------------------------------------------
int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) {
task_t task = TaskPort();
@@ -221,14 +221,31 @@ int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) {
return -1;
int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info);
- DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx "
- ") => %i (start = 0x%8.8llx, size = 0x%8.8llx, "
- "permissions = %u)",
+ DNBLogThreadedIf(LOG_MEMORY,
+ "MachTask::GetMemoryRegionInfo ( addr = 0x%8.8llx ) => %i "
+ "(start = 0x%8.8llx, size = 0x%8.8llx, permissions = %u)",
(uint64_t)addr, ret, (uint64_t)region_info->addr,
(uint64_t)region_info->size, region_info->permissions);
return ret;
}
+//----------------------------------------------------------------------
+// MachTask::GetMemoryTags
+//----------------------------------------------------------------------
+nub_bool_t MachTask::GetMemoryTags(nub_addr_t addr, nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ task_t task = TaskPort();
+ if (task == TASK_NULL)
+ return false;
+
+ bool ok = m_vm_memory.GetMemoryTags(task, addr, size, tags);
+ DNBLogThreadedIf(LOG_MEMORY, "MachTask::GetMemoryTags ( addr = 0x%8.8llx, "
+ "size = 0x%8.8llx ) => %s ( tag count = %llu)",
+ (uint64_t)addr, (uint64_t)size, (ok ? "ok" : "err"),
+ (uint64_t)tags.size());
+ return ok;
+}
+
#define TIME_VALUE_TO_TIMEVAL(a, r) \
do { \
(r)->tv_sec = (a)->seconds; \
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
index f3aa4d7d980f..bb57245e7da4 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp
@@ -13,6 +13,7 @@
#include "MachVMMemory.h"
#include "DNBLog.h"
#include "MachVMRegion.h"
+#include <cassert>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
@@ -123,6 +124,7 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
region_info->addr = vmRegion.StartAddress();
region_info->size = vmRegion.GetByteSize();
region_info->permissions = vmRegion.GetDNBPermissions();
+ region_info->flags = vmRegion.GetFlags();
region_info->dirty_pages =
get_dirty_pages(task, vmRegion.StartAddress(), vmRegion.GetByteSize());
region_info->vm_types = vmRegion.GetMemoryTypes();
@@ -150,6 +152,63 @@ nub_bool_t MachVMMemory::GetMemoryRegionInfo(task_t task, nub_addr_t address,
return true;
}
+// API availability:
+// mach_vm_update_pointers_with_remote_tags() - 26.0
+// VM_OFFSET_LIST_MAX macro - 26.1
+#ifndef VM_OFFSET_LIST_MAX
+#define VM_OFFSET_LIST_MAX 512
+#endif
+using mach_vm_offset_list_t = mach_vm_offset_t *;
+using mach_vm_update_pointers_with_remote_tags_t = kern_return_t(
+ mach_port_name_t target, mach_vm_offset_list_t in_pointer_list,
+ mach_msg_type_number_t in_pointer_listCnt,
+ mach_vm_offset_list_t out_pointer_list,
+ mach_msg_type_number_t *out_pointer_listCnt);
+
+nub_bool_t MachVMMemory::GetMemoryTags(task_t task, nub_addr_t address,
+ nub_size_t size,
+ std::vector<uint8_t> &tags) {
+ static auto mach_vm_update_pointers_with_remote_tags =
+ (mach_vm_update_pointers_with_remote_tags_t *)dlsym(
+ RTLD_DEFAULT, "mach_vm_update_pointers_with_remote_tags");
+ assert(mach_vm_update_pointers_with_remote_tags);
+
+ // Max batch size supported by mach_vm_update_pointers_with_remote_tags.
+ constexpr uint32_t max_ptr_count = VM_OFFSET_LIST_MAX;
+ constexpr uint32_t tag_shift = 56;
+ constexpr nub_addr_t tag_mask =
+ ((nub_addr_t)0x0f << tag_shift); // Lower half of top byte.
+ constexpr uint32_t tag_granule = 16;
+
+ mach_msg_type_number_t ptr_count =
+ (size / tag_granule) + ((size % tag_granule > 0) ? 1 : 0);
+ ptr_count = std::min(ptr_count, max_ptr_count);
+
+ auto ptr_arr = std::make_unique<mach_vm_offset_t[]>(ptr_count);
+ for (size_t i = 0; i < ptr_count; i++)
+ ptr_arr[i] = (address + i * tag_granule);
+
+ mach_msg_type_number_t ptr_count_out = ptr_count;
+ m_err = mach_vm_update_pointers_with_remote_tags(
+ task, ptr_arr.get(), ptr_count, ptr_arr.get(), &ptr_count_out);
+
+ const bool failed = (m_err.Fail() || (ptr_count != ptr_count_out));
+ if (failed || DNBLogCheckLogBit(LOG_MEMORY))
+ m_err.LogThreaded("::mach_vm_update_pointers_with_remote_tags ( task = "
+ "0x%4.4x, ptr_count = %d ) => %i ( ptr_count_out = %d)",
+ task, ptr_count, m_err.Status(), ptr_count_out);
+ if (failed)
+ return false;
+
+ tags.reserve(ptr_count);
+ for (size_t i = 0; i < ptr_count; i++) {
+ nub_addr_t tag = (ptr_arr[i] & tag_mask) >> tag_shift;
+ tags.push_back(tag);
+ }
+
+ return true;
+}
+
static uint64_t GetPhysicalMemory() {
// This doesn't change often at all. No need to poll each time.
static uint64_t physical_memory = 0;
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
index 05d2c029b998..8a7616091fbb 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h
@@ -28,6 +28,8 @@ public:
nub_size_t PageSize(task_t task);
nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address,
DNBRegionInfo *region_info);
+ nub_bool_t GetMemoryTags(task_t task, nub_addr_t address, nub_size_t size,
+ std::vector<uint8_t> &tags);
nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task,
struct task_basic_info ti, cpu_type_t cputype,
nub_process_t pid, vm_statistics64_data_t &vminfo,
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
index 97908b4acaf2..9d0d60fdaaed 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.cpp
@@ -114,6 +114,11 @@ bool MachVMRegion::RestoreProtections() {
return false;
}
+#ifdef VM_REGION_FLAG_JIT_ENABLED
+#define VM_REGION_HAS_FLAGS 1
+#else
+#define VM_REGION_HAS_FLAGS 0
+#endif
bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
// Restore any original protections and clear our vars
Clear();
@@ -140,6 +145,30 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
if (failed)
return false;
if (log_protections) {
+#if VM_REGION_HAS_FLAGS
+ DNBLogThreaded("info = { prot = %u, "
+ "max_prot = %u, "
+ "inheritance = 0x%8.8x, "
+ "offset = 0x%8.8llx, "
+ "user_tag = 0x%8.8x, "
+ "ref_count = %u, "
+ "shadow_depth = %u, "
+ "ext_pager = %u, "
+ "share_mode = %u, "
+ "is_submap = %d, "
+ "behavior = %d, "
+ "object_id = 0x%8.8x, "
+ "user_wired_count = 0x%4.4x, "
+ "flags = %d }",
+ m_data.protection, m_data.max_protection, m_data.inheritance,
+ (uint64_t)m_data.offset, m_data.user_tag, m_data.ref_count,
+ m_data.shadow_depth, m_data.external_pager,
+ m_data.share_mode, m_data.is_submap, m_data.behavior,
+ m_data.object_id, m_data.user_wired_count, m_data.flags);
+#else
+ // Duplicate log call instead of #if-defing printing of flags to avoid
+ // compiler warning: 'embedding a directive within macro arguments has
+ // undefined behavior'
DNBLogThreaded("info = { prot = %u, "
"max_prot = %u, "
"inheritance = 0x%8.8x, "
@@ -158,6 +187,7 @@ bool MachVMRegion::GetRegionForAddress(nub_addr_t addr) {
m_data.shadow_depth, m_data.external_pager,
m_data.share_mode, m_data.is_submap, m_data.behavior,
m_data.object_id, m_data.user_wired_count);
+#endif
}
m_curr_protection = m_data.protection;
@@ -183,6 +213,22 @@ uint32_t MachVMRegion::GetDNBPermissions() const {
return dnb_permissions;
}
+#ifndef VM_REGION_FLAG_MTE_ENABLED
+#define VM_REGION_FLAG_MTE_ENABLED 0x4
+#endif
+std::vector<std::string> MachVMRegion::GetFlags() const {
+ std::vector<std::string> flags;
+#if VM_REGION_HAS_FLAGS
+ if (m_data.flags & VM_REGION_FLAG_JIT_ENABLED)
+ flags.push_back("jit");
+ if (m_data.flags & VM_REGION_FLAG_TPRO_ENABLED)
+ flags.push_back("tpro");
+ if (m_data.flags & VM_REGION_FLAG_MTE_ENABLED)
+ flags.push_back("mt");
+#endif
+ return flags;
+}
+
std::vector<std::string> MachVMRegion::GetMemoryTypes() const {
std::vector<std::string> types;
if (m_data.user_tag == VM_MEMORY_STACK) {
diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
index cb7705893c7e..ba6e1f3bfa70 100644
--- a/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
+++ b/lldb/tools/debugserver/source/MacOSX/MachVMRegion.h
@@ -40,9 +40,10 @@ public:
vm_prot_t prot);
bool RestoreProtections();
bool GetRegionForAddress(nub_addr_t addr);
- std::vector<std::string> GetMemoryTypes() const;
uint32_t GetDNBPermissions() const;
+ std::vector<std::string> GetFlags() const;
+ std::vector<std::string> GetMemoryTypes() const;
const DNBError &GetError() { return m_err; }
diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp
index d9fb22c6a1c0..b2d79377f1ee 100644
--- a/lldb/tools/debugserver/source/RNBRemote.cpp
+++ b/lldb/tools/debugserver/source/RNBRemote.cpp
@@ -22,6 +22,9 @@
#include <mach/mach_vm.h>
#include <mach/task_info.h>
#include <memory>
+#if __has_include(<os/security_config.h>)
+#include <os/security_config.h>
+#endif
#include <pwd.h>
#include <string>
#include <sys/stat.h>
@@ -90,11 +93,34 @@ static const std::string JSON_ASYNC_TYPE_KEY_NAME("type");
std::setfill(' ') << std::setw((iword_idx)) << ""
#define INDENT_WITH_TABS(iword_idx) \
std::setfill('\t') << std::setw((iword_idx)) << ""
-// Class to handle communications via gdb remote protocol.
-// Prototypes
+// If `ch` is a meta character as per the binary packet convention in the
+// gdb-remote protocol, quote it and write it into `stream`, otherwise write it
+// as is.
+static void binary_encode_char(std::ostringstream &stream, char ch) {
+ if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
+ stream.put('}');
+ stream.put(ch ^ 0x20);
+ } else {
+ stream.put(ch);
+ }
+}
+
+// Equivalent to calling binary_encode_char for every element of `data`.
+static void binary_encode_data_vector(std::ostringstream &stream,
+ std::vector<uint8_t> data) {
+ for (auto ch : data)
+ binary_encode_char(stream, ch);
+}
-static std::string binary_encode_string(const std::string &s);
+// Quote any meta characters in a std::string as per the binary
+// packet convention in the gdb-remote protocol.
+static std::string binary_encode_string(const std::string &s) {
+ std::ostringstream stream;
+ for (char ch : s)
+ binary_encode_char(stream, ch);
+ return stream.str();
+}
// Decode a single hex character and return the hex value as a number or
// -1 if "ch" is not a hex character.
@@ -136,16 +162,30 @@ static std::string decode_hex_ascii_string(const char *p,
return arg;
}
-uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
- uint64_t fail_value = 0) {
+static uint64_t decode_uint64(const char *p, int base, char **end = nullptr,
+ uint64_t fail_value = 0) {
nub_addr_t addr = strtoull(p, end, 16);
if (addr == 0 && errno != 0)
return fail_value;
return addr;
}
-void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
- bool swap) {
+/// Attempts to parse a prefix of `number_str` as a uint64_t. If
+/// successful, the number is returned and the prefix is dropped from
+/// `number_str`.
+static std::optional<uint64_t> extract_u64(std::string_view &number_str) {
+ char *str_end = nullptr;
+ errno = 0;
+ uint64_t number = strtoull(number_str.data(), &str_end, 16);
+ if (errno != 0)
+ return std::nullopt;
+ assert(str_end);
+ number_str.remove_prefix(str_end - number_str.data());
+ return number;
+}
+
+static void append_hex_value(std::ostream &ostrm, const void *buf,
+ size_t buf_size, bool swap) {
int i;
const uint8_t *p = (const uint8_t *)buf;
if (swap) {
@@ -157,7 +197,7 @@ void append_hex_value(std::ostream &ostrm, const void *buf, size_t buf_size,
}
}
-std::string cstring_to_asciihex_string(const char *str) {
+static std::string cstring_to_asciihex_string(const char *str) {
std::string hex_str;
hex_str.reserve(strlen(str) * 2);
while (str && *str) {
@@ -169,7 +209,8 @@ std::string cstring_to_asciihex_string(const char *str) {
return hex_str;
}
-void append_hexified_string(std::ostream &ostrm, const std::string &string) {
+static void append_hexified_string(std::ostream &ostrm,
+ const std::string &string) {
size_t string_size = string.size();
const char *string_buf = string.c_str();
for (size_t i = 0; i < string_size; i++) {
@@ -177,6 +218,25 @@ void append_hexified_string(std::ostream &ostrm, const std::string &string) {
}
}
+/// Returns true if `str` starts with `prefix`.
+static bool starts_with(std::string_view str, std::string_view prefix) {
+ return str.substr(0, prefix.size()) == prefix;
+}
+
+/// Splits `list_str` into multiple string_views separated by `,`.
+static std::vector<std::string_view>
+parse_comma_separated_list(std::string_view list_str) {
+ std::vector<std::string_view> list;
+ while (!list_str.empty()) {
+ auto pos = list_str.find(',');
+ list.push_back(list_str.substr(0, pos));
+ if (pos == list_str.npos)
+ break;
+ list_str.remove_prefix(pos + 1);
+ }
+ return list;
+}
+
// from System.framework/Versions/B/PrivateHeaders/sys/codesign.h
extern "C" {
#define CS_OPS_STATUS 0 /* return status */
@@ -243,6 +303,11 @@ void RNBRemote::CreatePacketTable() {
"Read memory"));
t.push_back(Packet(read_register, &RNBRemote::HandlePacket_p, NULL, "p",
"Read one register"));
+ // Careful: this *must* come before the `M` packet, as debugserver matches
+ // packet prefixes against known packet names. Inverting the order would match
+ // `MultiMemRead` as an `M` packet.
+ t.push_back(Packet(multi_mem_read, &RNBRemote::HandlePacket_MultiMemRead,
+ NULL, "MultiMemRead", "Read multiple memory addresses"));
t.push_back(Packet(write_memory, &RNBRemote::HandlePacket_M, NULL, "M",
"Write memory"));
t.push_back(Packet(write_register, &RNBRemote::HandlePacket_P, NULL, "P",
@@ -502,6 +567,8 @@ void RNBRemote::CreatePacketTable() {
memory_region_info, &RNBRemote::HandlePacket_MemoryRegionInfo, NULL,
"qMemoryRegionInfo", "Return size and attributes of a memory region that "
"contains the given address"));
+ t.push_back(Packet(get_memory_tags, &RNBRemote::HandlePacket_qMemTags, NULL,
+ "qMemTags", "Return tags for a region of memory"));
t.push_back(Packet(get_profile_data, &RNBRemote::HandlePacket_GetProfileData,
NULL, "qGetProfileData",
"Return profiling data of the current target."));
@@ -1022,8 +1089,6 @@ rnb_err_t RNBRemote::HandleAsyncPacket(PacketEnum *type) {
rnb_err_t RNBRemote::HandleReceivedPacket(PacketEnum *type) {
static DNBTimer g_packetTimer(true);
- // DNBLogThreadedIf (LOG_RNB_REMOTE, "%8u RNBRemote::%s",
- // (uint32_t)m_comm.Timer().ElapsedMicroSeconds(true), __FUNCTION__);
rnb_err_t err = rnb_err;
std::string packet_data;
RNBRemote::Packet packet_info;
@@ -1279,8 +1344,7 @@ static cpu_type_t best_guess_cpu_type() {
LEN is the number of bytes to be processed. If a character is escaped,
it is 2 characters for LEN. A LEN of -1 means decode-until-nul-byte
(end of string). */
-
-std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
+static std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
std::vector<uint8_t> bytes;
if (len == 0) {
return bytes;
@@ -1299,31 +1363,10 @@ std::vector<uint8_t> decode_binary_data(const char *str, size_t len) {
return bytes;
}
-// Quote any meta characters in a std::string as per the binary
-// packet convention in the gdb-remote protocol.
-
-static std::string binary_encode_string(const std::string &s) {
- std::string output;
- const size_t s_size = s.size();
- const char *s_chars = s.c_str();
-
- for (size_t i = 0; i < s_size; i++) {
- unsigned char ch = *(s_chars + i);
- if (ch == '#' || ch == '$' || ch == '}' || ch == '*') {
- output.push_back('}'); // 0x7d
- output.push_back(ch ^ 0x20);
- } else {
- output.push_back(ch);
- }
- }
- return output;
-}
-
// If the value side of a key-value pair in JSON is a string,
// and that string has a " character in it, the " character must
// be escaped.
-
-std::string json_string_quote_metachars(const std::string &s) {
+static std::string json_string_quote_metachars(const std::string &s) {
if (s.find('"') == std::string::npos)
return s;
@@ -1457,15 +1500,6 @@ bool RNBRemote::InitializeRegisters(bool force) {
}
}
- // for (auto &reg_entry: g_dynamic_register_map)
- // {
- // DNBLogThreaded("%4i: size = %3u, pseudo = %i, name = %s",
- // reg_entry.offset,
- // reg_entry.nub_info.size,
- // reg_entry.nub_info.value_regs != NULL,
- // reg_entry.nub_info.name);
- // }
-
g_reg_entries = g_dynamic_register_map.data();
g_num_reg_entries = g_dynamic_register_map.size();
}
@@ -1714,7 +1748,7 @@ rnb_err_t RNBRemote::HandlePacket_qThreadExtraInfo(const char *p) {
return SendPacket("");
}
-const char *k_space_delimiters = " \t";
+static const char *k_space_delimiters = " \t";
static void skip_spaces(std::string &line) {
if (!line.empty()) {
size_t space_pos = line.find_first_not_of(k_space_delimiters);
@@ -2019,7 +2053,7 @@ rnb_err_t RNBRemote::HandlePacket_qRegisterInfo(const char *p) {
QSetLogging:bitmask=LOG_ALL;mode=asl;
*/
-rnb_err_t set_logging(const char *p) {
+static rnb_err_t set_logging(const char *p) {
int bitmask = 0;
while (p && *p != '\0') {
if (strncmp(p, "bitmask=", sizeof("bitmask=") - 1) == 0) {
@@ -2563,11 +2597,10 @@ rnb_err_t RNBRemote::HandlePacket_QSetProcessEvent(const char *p) {
// If a fail_value is provided, a correct-length reply is always provided,
// even if the register cannot be read right now on this thread.
-bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
- nub_thread_t tid,
- const register_map_entry_t *reg,
- const DNBRegisterValue *reg_value_ptr,
- std::optional<uint8_t> fail_value) {
+static bool register_value_in_hex_fixed_width(
+ std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
+ const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr,
+ std::optional<uint8_t> fail_value) {
if (reg != NULL) {
std::unique_ptr<DNBRegisterValue> reg_value =
std::make_unique<DNBRegisterValue>();
@@ -2594,7 +2627,7 @@ bool register_value_in_hex_fixed_width(std::ostream &ostrm, nub_process_t pid,
return false;
}
-void debugserver_regnum_with_fixed_width_hex_register_value(
+static void debugserver_regnum_with_fixed_width_hex_register_value(
std::ostream &ostrm, nub_process_t pid, nub_thread_t tid,
const register_map_entry_t *reg, const DNBRegisterValue *reg_value_ptr,
std::optional<uint8_t> fail_value) {
@@ -3155,6 +3188,88 @@ rnb_err_t RNBRemote::HandlePacket_m(const char *p) {
return SendPacket(ostrm.str());
}
+rnb_err_t RNBRemote::HandlePacket_MultiMemRead(const char *p) {
+ const std::string_view packet_name("MultiMemRead:");
+ std::string_view packet(p);
+
+ if (!starts_with(packet, packet_name))
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid MultiMemRead packet prefix");
+
+ packet.remove_prefix(packet_name.size());
+
+ const std::string_view ranges_prefix("ranges:");
+ if (!starts_with(packet, ranges_prefix))
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "Missing 'ranges' in MultiMemRead packet");
+ packet.remove_prefix(ranges_prefix.size());
+
+ std::vector<std::pair<nub_addr_t, std::size_t>> ranges;
+ std::size_t total_length = 0;
+
+ // Ranges should have the form: <addr>,<size>[,<addr>,<size>]*;
+ auto end_of_ranges_pos = packet.find(';');
+ if (end_of_ranges_pos == packet.npos)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "MultiMemRead missing end of ranges marker");
+
+ std::vector<std::string_view> numbers_list =
+ parse_comma_separated_list(packet.substr(0, end_of_ranges_pos));
+ packet.remove_prefix(end_of_ranges_pos + 1);
+
+ // Ranges are pairs, so the number of elements must be even.
+ if (numbers_list.size() % 2 == 1)
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p,
+ "MultiMemRead has an odd number of numbers for the ranges");
+
+ for (unsigned idx = 0; idx < numbers_list.size(); idx += 2) {
+ std::optional<uint64_t> maybe_addr = extract_u64(numbers_list[idx]);
+ std::optional<uint64_t> maybe_length = extract_u64(numbers_list[idx + 1]);
+ if (!maybe_addr || !maybe_length)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "Invalid MultiMemRead range");
+ // A sanity check that the packet requested is not too large or a negative
+ // number.
+ if (*maybe_length > 4 * 1024 * 1024)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, packet.data(),
+ "MultiMemRead length is too large");
+
+ ranges.emplace_back(*maybe_addr, *maybe_length);
+ total_length += *maybe_length;
+ }
+
+ if (ranges.empty())
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "MultiMemRead has an empty range list");
+
+ if (!packet.empty())
+ return HandlePacket_ILLFORMED(
+ __FILE__, __LINE__, p, "MultiMemRead packet has unrecognized fields");
+
+ std::vector<std::vector<uint8_t>> buffers;
+ buffers.reserve(ranges.size());
+ for (auto [base_addr, length] : ranges) {
+ buffers.emplace_back(length, 0);
+ nub_size_t bytes_read = DNBProcessMemoryRead(m_ctx.ProcessID(), base_addr,
+ length, buffers.back().data());
+ buffers.back().resize(bytes_read);
+ }
+
+ std::ostringstream reply_stream;
+ bool first = true;
+ for (const std::vector<uint8_t> &buffer : buffers) {
+ reply_stream << (first ? "" : ",") << std::hex << buffer.size();
+ first = false;
+ }
+ reply_stream << ';';
+
+ for (const std::vector<uint8_t> &buffer : buffers)
+ binary_encode_data_vector(reply_stream, buffer);
+
+ return SendPacket(reply_stream.str());
+}
+
// Read memory, sent it up as binary data.
// Usage: xADDR,LEN
// ADDR and LEN are both base 16.
@@ -3211,21 +3326,9 @@ rnb_err_t RNBRemote::HandlePacket_x(const char *p) {
return SendErrorPacket("E80");
}
- std::vector<uint8_t> buf_quoted;
- buf_quoted.reserve(bytes_read + 30);
- for (nub_size_t i = 0; i < bytes_read; i++) {
- if (buf[i] == '#' || buf[i] == '$' || buf[i] == '}' || buf[i] == '*') {
- buf_quoted.push_back(0x7d);
- buf_quoted.push_back(buf[i] ^ 0x20);
- } else {
- buf_quoted.push_back(buf[i]);
- }
- }
- length = buf_quoted.size();
-
+ buf.resize(bytes_read);
std::ostringstream ostrm;
- for (unsigned long i = 0; i < length; i++)
- ostrm << buf_quoted[i];
+ binary_encode_data_vector(ostrm, buf);
return SendPacket(ostrm.str());
}
@@ -3475,6 +3578,18 @@ static bool GetProcessNameFrom_vAttach(const char *&p,
return return_val;
}
+static bool supports_memory_tagging() {
+ const char *name = "hw.optional.arm.FEAT_MTE4";
+ uint32_t val;
+ size_t len = sizeof(val);
+ int ret = ::sysctlbyname(name, &val, &len, nullptr, 0);
+ if (ret != 0)
+ return false;
+
+ assert(len == sizeof(val));
+ return val;
+}
+
rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
uint32_t max_packet_size = 128 * 1024; // 128 KiB is a reasonable max packet
// size--debugger can always use less
@@ -3505,6 +3620,10 @@ rnb_err_t RNBRemote::HandlePacket_qSupported(const char *p) {
reply << "SupportedWatchpointTypes=x86_64;";
#endif
+ if (supports_memory_tagging())
+ reply << "memory-tagging+;";
+
+ reply << "MultiMemRead+;";
return SendPacket(reply.str().c_str());
}
@@ -4251,7 +4370,6 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
is in unmapped memory
Region lookup cannot be performed on this platform or process is not
yet launched
- This packet isn't implemented
Examples of use:
qMemoryRegionInfo:3a55140
@@ -4303,6 +4421,16 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
ostrm << 'x';
ostrm << ';';
+ if (!region_info.flags.empty()) {
+ ostrm << "flags:";
+ for (size_t i = 0; i < region_info.flags.size(); i++) {
+ if (i != 0)
+ ostrm << " "; // Separator is whitespace
+ ostrm << region_info.flags[i];
+ }
+ ostrm << ";";
+ }
+
ostrm << "dirty-pages:";
if (region_info.dirty_pages.size() > 0) {
bool first = true;
@@ -4327,6 +4455,62 @@ rnb_err_t RNBRemote::HandlePacket_MemoryRegionInfo(const char *p) {
return SendPacket(ostrm.str());
}
+// qMemTags:<hex address>,<hex length>:<hex type>
+rnb_err_t RNBRemote::HandlePacket_qMemTags(const char *p) {
+ nub_process_t pid = m_ctx.ProcessID();
+ if (pid == INVALID_NUB_PROCESS)
+ return SendPacket("OK");
+
+ StdStringExtractor packet(p);
+ packet.SetFilePos(strlen("qMemTags:"));
+
+ // Address
+ nub_addr_t addr =
+ packet.GetHexMaxU64(StdStringExtractor::BigEndian, INVALID_NUB_ADDRESS);
+ if (addr == INVALID_NUB_ADDRESS)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing address in qMemTags packet");
+ // ,
+ if (packet.GetChar() != ',')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Length
+ uint64_t length = packet.GetHexMaxU64(StdStringExtractor::BigEndian, 0);
+ if (length == 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing length in qMemTags packet");
+ // :
+ if (packet.GetChar() != ':')
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+ // Type
+ // On the LLDB side this is a `int32_t` serialized as (unsigned) hex, which
+ // means negative values will show up as large positive values here. Right
+ // now, we only support MTE (type 1), so we can ignore this complication.
+ uint32_t type = packet.GetHexMaxU32(StdStringExtractor::BigEndian, 0);
+ if (type != 1 /* MTE */)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid/missing type in qMemTags packet, "
+ "only MTE (type 1) is supported");
+ // <EOF>
+ if (packet.GetBytesLeft() != 0)
+ return HandlePacket_ILLFORMED(__FILE__, __LINE__, p,
+ "Invalid qMemTags packet format");
+
+ std::vector<uint8_t> tags;
+ bool ok = DNBProcessGetMemoryTags(pid, addr, length, tags);
+ if (!ok)
+ return SendErrorPacket("E91");
+
+ std::ostringstream ostrm;
+ ostrm << "m"; // Multi part replies
+ for (uint8_t tag : tags) {
+ ostrm << RAWHEX8(tag); // 2 hex chars per tag
+ }
+
+ return SendPacket(ostrm.str());
+}
+
// qGetProfileData;scan_type:0xYYYYYYY
rnb_err_t RNBRemote::HandlePacket_GetProfileData(const char *p) {
nub_process_t pid = m_ctx.ProcessID();
@@ -4817,8 +5001,8 @@ rnb_err_t RNBRemote::HandlePacket_qHostInfo(const char *p) {
return SendPacket(strm.str());
}
-void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
- bool has_attributes) {
+static void XMLElementStart(std::ostringstream &s, uint32_t indent,
+ const char *name, bool has_attributes) {
if (indent)
s << INDENT_WITH_SPACES(indent);
s << '<' << name;
@@ -4826,43 +5010,22 @@ void XMLElementStart(std::ostringstream &s, uint32_t indent, const char *name,
s << '>' << std::endl;
}
-void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
+static void XMLElementStartEndAttributes(std::ostringstream &s, bool empty) {
if (empty)
s << '/';
s << '>' << std::endl;
}
-void XMLElementEnd(std::ostringstream &s, uint32_t indent, const char *name) {
+static void XMLElementEnd(std::ostringstream &s, uint32_t indent,
+ const char *name) {
if (indent)
s << INDENT_WITH_SPACES(indent);
s << '<' << '/' << name << '>' << std::endl;
}
-void XMLElementWithStringValue(std::ostringstream &s, uint32_t indent,
- const char *name, const char *value,
- bool close = true) {
- if (value) {
- if (indent)
- s << INDENT_WITH_SPACES(indent);
- s << '<' << name << '>' << value;
- if (close)
- XMLElementEnd(s, 0, name);
- }
-}
-
-void XMLElementWithUnsignedValue(std::ostringstream &s, uint32_t indent,
- const char *name, uint64_t value,
- bool close = true) {
- if (indent)
- s << INDENT_WITH_SPACES(indent);
-
- s << '<' << name << '>' << DECIMAL << value;
- if (close)
- XMLElementEnd(s, 0, name);
-}
-
-void XMLAttributeString(std::ostringstream &s, const char *name,
- const char *value, const char *default_value = NULL) {
+static void XMLAttributeString(std::ostringstream &s, const char *name,
+ const char *value,
+ const char *default_value = NULL) {
if (value) {
if (default_value && strcmp(value, default_value) == 0)
return; // No need to emit the attribute because it matches the default
@@ -4871,15 +5034,16 @@ void XMLAttributeString(std::ostringstream &s, const char *name,
}
}
-void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
- uint64_t value) {
+static void XMLAttributeUnsignedDecimal(std::ostringstream &s, const char *name,
+ uint64_t value) {
s << ' ' << name << "=\"" << DECIMAL << value << "\"";
}
-void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
- nub_size_t num_reg_sets,
- const DNBRegisterSetInfo *reg_set_info,
- const register_map_entry_t &reg) {
+static void GenerateTargetXMLRegister(std::ostringstream &s,
+ const uint32_t reg_num,
+ nub_size_t num_reg_sets,
+ const DNBRegisterSetInfo *reg_set_info,
+ const register_map_entry_t &reg) {
const char *default_lldb_encoding = "uint";
const char *lldb_encoding = default_lldb_encoding;
const char *gdb_group = "general";
@@ -5050,7 +5214,7 @@ void GenerateTargetXMLRegister(std::ostringstream &s, const uint32_t reg_num,
XMLElementStartEndAttributes(s, true);
}
-void GenerateTargetXMLRegisters(std::ostringstream &s) {
+static void GenerateTargetXMLRegisters(std::ostringstream &s) {
nub_size_t num_reg_sets = 0;
const DNBRegisterSetInfo *reg_sets = DNBGetRegisterSetInfo(&num_reg_sets);
@@ -5089,7 +5253,7 @@ static const char *g_target_xml_footer = "</target>";
static std::string g_target_xml;
-void UpdateTargetXML() {
+static void UpdateTargetXML() {
std::ostringstream s;
s << g_target_xml_header << std::endl;
@@ -5224,8 +5388,9 @@ rnb_err_t RNBRemote::HandlePacket_jGetDyldProcessState(const char *p) {
// a one-level-deep JSON dictionary of key-value pairs. e.g.
// jThreadExtendedInfo:{"plo_pthread_tsd_base_address_offset":0,"plo_pthread_tsd_base_offset":224,"plo_pthread_tsd_entry_size":8,"thread":144305}]
//
-uint64_t get_integer_value_for_key_name_from_json(const char *key,
- const char *json_string) {
+static uint64_t
+get_integer_value_for_key_name_from_json(const char *key,
+ const char *json_string) {
uint64_t retval = INVALID_NUB_ADDRESS;
std::string key_with_quotes = "\"";
key_with_quotes += key;
@@ -5261,9 +5426,9 @@ uint64_t get_integer_value_for_key_name_from_json(const char *key,
// Returns true if it was able to find the key name, and sets the 'value'
// argument to the value found.
-bool get_boolean_value_for_key_name_from_json(const char *key,
- const char *json_string,
- bool &value) {
+static bool get_boolean_value_for_key_name_from_json(const char *key,
+ const char *json_string,
+ bool &value) {
std::string key_with_quotes = "\"";
key_with_quotes += key;
key_with_quotes += "\"";
@@ -5300,7 +5465,7 @@ bool get_boolean_value_for_key_name_from_json(const char *key,
// Returns true if it was able to find the key name, false if it did not.
// "ints" will have all integers found in the array appended to it.
-bool get_array_of_ints_value_for_key_name_from_json(
+static bool get_array_of_ints_value_for_key_name_from_json(
const char *key, const char *json_string, std::vector<uint64_t> &ints) {
std::string key_with_quotes = "\"";
key_with_quotes += key;
@@ -6162,6 +6327,21 @@ GetCPUTypesFromHost(nub_process_t pid) {
return {cputype, cpusubtype};
}
+static bool ProcessRunningWithMemoryTagging(pid_t pid) {
+#if __has_include(<os/security_config.h>)
+ if (__builtin_available(macOS 26.0, iOS 26.0, tvOS 26.0, watchOS 26.0,
+ visionOS 26.0, driverkit 25.0, *)) {
+ os_security_config_t config;
+ int ret = ::os_security_config_get_for_proc(pid, &config);
+ if (ret != 0)
+ return false;
+
+ return (config & OS_SECURITY_CONFIG_MTE);
+ }
+#endif
+ return false;
+}
+
// Note that all numeric values returned by qProcessInfo are hex encoded,
// including the pid and the cpu type.
@@ -6338,6 +6518,9 @@ rnb_err_t RNBRemote::HandlePacket_qProcessInfo(const char *p) {
rep << "vendor:apple;";
+ if (ProcessRunningWithMemoryTagging(pid))
+ rep << "mte:enabled;";
+
#if defined(__LITTLE_ENDIAN__)
rep << "endian:little;";
#elif defined(__BIG_ENDIAN__)
diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h
index ad254ae90e2f..b32c00adc8b2 100644
--- a/lldb/tools/debugserver/source/RNBRemote.h
+++ b/lldb/tools/debugserver/source/RNBRemote.h
@@ -121,6 +121,7 @@ public:
set_list_threads_in_stop_reply, // 'QListThreadsInStopReply:'
sync_thread_state, // 'QSyncThreadState:'
memory_region_info, // 'qMemoryRegionInfo:'
+ get_memory_tags, // 'qMemTags:'
get_profile_data, // 'qGetProfileData'
set_enable_profiling, // 'QSetEnableAsyncProfiling'
enable_compression, // 'QEnableCompression:'
@@ -135,6 +136,7 @@ public:
query_transfer, // 'qXfer:'
json_query_dyld_process_state, // 'jGetDyldProcessState'
enable_error_strings, // 'QEnableErrorStrings'
+ multi_mem_read, // 'MultiMemRead'
unknown_type
};
// clang-format on
@@ -215,6 +217,7 @@ public:
rnb_err_t HandlePacket_last_signal(const char *p);
rnb_err_t HandlePacket_m(const char *p);
rnb_err_t HandlePacket_M(const char *p);
+ rnb_err_t HandlePacket_MultiMemRead(const char *p);
rnb_err_t HandlePacket_x(const char *p);
rnb_err_t HandlePacket_X(const char *p);
rnb_err_t HandlePacket_z(const char *p);
@@ -237,6 +240,7 @@ public:
rnb_err_t HandlePacket_SaveRegisterState(const char *p);
rnb_err_t HandlePacket_RestoreRegisterState(const char *p);
rnb_err_t HandlePacket_MemoryRegionInfo(const char *p);
+ rnb_err_t HandlePacket_qMemTags(const char *p);
rnb_err_t HandlePacket_GetProfileData(const char *p);
rnb_err_t HandlePacket_SetEnableAsyncProfiling(const char *p);
rnb_err_t HandlePacket_QEnableCompression(const char *p);
diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt
index 446bf68d0962..67956af7fe3f 100644
--- a/lldb/tools/driver/CMakeLists.txt
+++ b/lldb/tools/driver/CMakeLists.txt
@@ -34,6 +34,10 @@ add_dependencies(lldb
${tablegen_deps}
)
+if(DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH)
+ target_compile_definitions(lldb PRIVATE LLDB_PYTHON_DLL_RELATIVE_PATH="${LLDB_PYTHON_DLL_RELATIVE_PATH}")
+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.
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index 16cc736441b5..ba0041111045 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -22,7 +22,10 @@
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
#include "lldb/Utility/Status.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
@@ -30,6 +33,10 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
+#ifdef _WIN32
+#include "llvm/Support/Windows/WindowsSupport.h"
+#endif
+
#include <algorithm>
#include <atomic>
#include <bitset>
@@ -426,6 +433,47 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
return error;
}
+#if defined(_WIN32) && defined(LLDB_PYTHON_DLL_RELATIVE_PATH)
+/// Returns the full path to the lldb.exe executable.
+inline std::wstring GetPathToExecutableW() {
+ // Iterate until we reach the Windows API maximum path length (32,767).
+ std::vector<WCHAR> buffer;
+ buffer.resize(MAX_PATH /*=260*/);
+ while (buffer.size() < 32767) {
+ if (GetModuleFileNameW(NULL, buffer.data(), buffer.size()) < buffer.size())
+ return std::wstring(buffer.begin(), buffer.end());
+ buffer.resize(buffer.size() * 2);
+ }
+ return L"";
+}
+
+/// Resolve the full path of the directory defined by
+/// LLDB_PYTHON_DLL_RELATIVE_PATH. If it exists, add it to the list of DLL
+/// search directories.
+void AddPythonDLLToSearchPath() {
+ std::wstring modulePath = GetPathToExecutableW();
+ if (modulePath.empty()) {
+ llvm::errs() << "error: unable to find python.dll." << '\n';
+ return;
+ }
+
+ SmallVector<char, MAX_PATH> utf8Path;
+ if (sys::windows::UTF16ToUTF8(modulePath.c_str(), modulePath.length(),
+ utf8Path))
+ return;
+ sys::path::remove_filename(utf8Path);
+ sys::path::append(utf8Path, LLDB_PYTHON_DLL_RELATIVE_PATH);
+ sys::fs::make_absolute(utf8Path);
+
+ SmallVector<wchar_t, 1> widePath;
+ if (sys::windows::widenPath(utf8Path.data(), widePath))
+ return;
+
+ if (sys::fs::exists(utf8Path))
+ SetDllDirectoryW(widePath.data());
+}
+#endif
+
std::string EscapeString(std::string arg) {
std::string::size_type pos = 0;
while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
@@ -728,6 +776,10 @@ int main(int argc, char const *argv[]) {
"~/Library/Logs/DiagnosticReports/.\n");
#endif
+#if defined(_WIN32) && defined(LLDB_PYTHON_DLL_RELATIVE_PATH)
+ AddPythonDLLToSearchPath();
+#endif
+
// Parse arguments.
LLDBOptTable T;
unsigned MissingArgIndex;
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 71681fd4b51e..a90ddf59671e 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -78,11 +78,9 @@ enum DAPBroadcasterBits {
enum class ReplMode { Variable = 0, Command, Auto };
-using DAPTransport =
- lldb_private::Transport<protocol::Request, protocol::Response,
- protocol::Event>;
+using DAPTransport = lldb_private::transport::JSONTransport<ProtocolDescriptor>;
-struct DAP final : private DAPTransport::MessageHandler {
+struct DAP final : public DAPTransport::MessageHandler {
/// Path to the lldb-dap binary itself.
static llvm::StringRef debug_adapter_path;
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index 773891353db6..e7d9b89653f1 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -57,7 +57,7 @@ SetupIORedirection(const std::vector<std::optional<std::string>> &stdio,
size_t n = std::max(stdio.size(), static_cast<size_t>(3));
for (size_t i = 0; i < n; i++) {
std::optional<std::string> path;
- if (stdio.size() < i)
+ if (stdio.size() <= i)
path = stdio.back();
else
path = stdio[i];
@@ -107,7 +107,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
arguments.configuration.program, arguments.args, arguments.env,
- arguments.cwd, comm_file.m_path, debugger_pid,
+ arguments.cwd, comm_file.m_path, debugger_pid, arguments.stdio,
arguments.console == protocol::eConsoleExternalTerminal);
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
std::move(reverse_request));
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 4f26599a49ba..71e91f8f41a6 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -866,7 +866,8 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit) {
llvm::json::Object CreateRunInTerminalReverseRequest(
llvm::StringRef program, const std::vector<std::string> &args,
const llvm::StringMap<std::string> &env, llvm::StringRef cwd,
- llvm::StringRef comm_file, lldb::pid_t debugger_pid, bool external) {
+ llvm::StringRef comm_file, lldb::pid_t debugger_pid,
+ const std::vector<std::optional<std::string>> &stdio, bool external) {
llvm::json::Object run_in_terminal_args;
if (external) {
// This indicates the IDE to open an external terminal window.
@@ -885,6 +886,18 @@ llvm::json::Object CreateRunInTerminalReverseRequest(
}
req_args.push_back("--launch-target");
req_args.push_back(program.str());
+ if (!stdio.empty()) {
+ req_args.push_back("--stdio");
+ std::stringstream ss;
+ for (const std::optional<std::string> &file : stdio) {
+ if (file)
+ ss << *file;
+ ss << ":";
+ }
+ std::string files = ss.str();
+ files.pop_back();
+ req_args.push_back(std::move(files));
+ }
req_args.insert(req_args.end(), args.begin(), args.end());
run_in_terminal_args.try_emplace("args", req_args);
diff --git a/lldb/tools/lldb-dap/JSONUtils.h b/lldb/tools/lldb-dap/JSONUtils.h
index e9094f67b94e..0c865a33a6ce 100644
--- a/lldb/tools/lldb-dap/JSONUtils.h
+++ b/lldb/tools/lldb-dap/JSONUtils.h
@@ -388,6 +388,10 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
/// launcher uses it on Linux tell the kernel that it should allow the
/// debugger process to attach.
///
+/// \param[in] stdio
+/// An array of file paths for redirecting the program's standard IO
+/// streams.
+///
/// \param[in] external
/// If set to true, the program will run in an external terminal window
/// instead of IDE's integrated terminal.
@@ -398,7 +402,8 @@ llvm::json::Value CreateCompileUnit(lldb::SBCompileUnit &unit);
llvm::json::Object CreateRunInTerminalReverseRequest(
llvm::StringRef program, const std::vector<std::string> &args,
const llvm::StringMap<std::string> &env, llvm::StringRef cwd,
- llvm::StringRef comm_file, lldb::pid_t debugger_pid, bool external);
+ llvm::StringRef comm_file, lldb::pid_t debugger_pid,
+ const std::vector<std::optional<std::string>> &stdio, bool external);
/// Create a "Terminated" JSON object that contains statistics
///
diff --git a/lldb/tools/lldb-dap/Options.td b/lldb/tools/lldb-dap/Options.td
index c8492c6a62b2..5e9dd7a1d641 100644
--- a/lldb/tools/lldb-dap/Options.td
+++ b/lldb/tools/lldb-dap/Options.td
@@ -47,6 +47,12 @@ def debugger_pid: S<"debugger-pid">,
HelpText<"The PID of the lldb-dap instance that sent the launchInTerminal "
"request when using --launch-target.">;
+def stdio: S<"stdio">,
+ MetaVarName<"<stdin:stdout:stderr:...>">,
+ HelpText<"An array of file paths for redirecting the program's standard IO "
+ "streams. A colon-separated list of entries. Empty value means no "
+ "redirection.">;
+
def repl_mode
: S<"repl-mode">,
MetaVarName<"<mode>">,
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
index 0a9ef538a739..92e41b1dbf59 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolBase.h
@@ -30,6 +30,8 @@ namespace lldb_dap::protocol {
// MARK: Base Protocol
+using Id = int64_t;
+
/// A client or debug adapter initiated request.
struct Request {
/// Sequence number of the message (also known as message ID). The `seq` for
@@ -39,7 +41,7 @@ struct Request {
/// associate requests with their corresponding responses. For protocol
/// messages of type `request` the sequence number can be used to cancel the
/// request.
- int64_t seq;
+ Id seq;
/// The command to execute.
std::string command;
@@ -76,7 +78,7 @@ enum ResponseMessage : unsigned {
/// Response for a request.
struct Response {
/// Sequence number of the corresponding request.
- int64_t request_seq;
+ Id request_seq;
/// The command requested.
std::string command;
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
index b455112cd37d..b9393356b4e0 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.cpp
@@ -461,7 +461,7 @@ bool fromJSON(const json::Value &Params, DataBreakpointInfoArguments &DBIA,
json::Path P) {
json::ObjectMapper O(Params, P);
return O && O.map("variablesReference", DBIA.variablesReference) &&
- O.map("name", DBIA.name) && O.map("frameId", DBIA.frameId) &&
+ O.map("name", DBIA.name) && O.mapOptional("frameId", DBIA.frameId) &&
O.map("bytes", DBIA.bytes) && O.map("asAddress", DBIA.asAddress) &&
O.map("mode", DBIA.mode);
}
diff --git a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
index 92dada229584..a85a68b87014 100644
--- a/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
+++ b/lldb/tools/lldb-dap/Protocol/ProtocolRequests.h
@@ -300,6 +300,7 @@ struct LaunchRequestArguments {
/// terminal or external terminal.
Console console = eConsoleInternal;
+ /// An array of file paths for redirecting the program's standard IO streams.
std::vector<std::optional<std::string>> stdio;
/// @}
diff --git a/lldb/tools/lldb-dap/ProtocolUtils.cpp b/lldb/tools/lldb-dap/ProtocolUtils.cpp
index 775c82fbb771..868c67ca7298 100644
--- a/lldb/tools/lldb-dap/ProtocolUtils.cpp
+++ b/lldb/tools/lldb-dap/ProtocolUtils.cpp
@@ -301,6 +301,14 @@ Variable CreateVariable(lldb::SBValue v, int64_t var_ref, bool format_hex,
if (lldb::addr_t addr = v.GetLoadAddress(); addr != LLDB_INVALID_ADDRESS)
var.memoryReference = addr;
+ bool is_readonly = v.GetType().IsAggregateType() ||
+ v.GetValueType() == lldb::eValueTypeRegisterSet;
+ if (is_readonly) {
+ if (!var.presentationHint)
+ var.presentationHint = {VariablePresentationHint()};
+ var.presentationHint->attributes.push_back("readOnly");
+ }
+
return var;
}
diff --git a/lldb/tools/lldb-dap/Transport.h b/lldb/tools/lldb-dap/Transport.h
index 4a9dd76c2303..58c48c133f9c 100644
--- a/lldb/tools/lldb-dap/Transport.h
+++ b/lldb/tools/lldb-dap/Transport.h
@@ -22,11 +22,18 @@
namespace lldb_dap {
+struct ProtocolDescriptor {
+ using Id = protocol::Id;
+ using Req = protocol::Request;
+ using Resp = protocol::Response;
+ using Evt = protocol::Event;
+};
+
/// A transport class that performs the Debug Adapter Protocol communication
/// with the client.
class Transport final
- : public lldb_private::HTTPDelimitedJSONTransport<
- protocol::Request, protocol::Response, protocol::Event> {
+ : public lldb_private::transport::HTTPDelimitedJSONTransport<
+ ProtocolDescriptor> {
public:
Transport(llvm::StringRef client_name, lldb_dap::Log *log,
lldb::IOObjectSP input, lldb::IOObjectSP output);
diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index e961c2e48b25..3f0f150c0d98 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -626,7 +626,10 @@
"stdio": {
"type": "array",
"items": {
- "type": "string"
+ "type": [
+ "string",
+ "null"
+ ]
},
"description": "The stdio property specifies the redirection targets for the debuggee's stdio streams. A null value redirects a stream to the default debug terminal. String can be a path to file, named pipe or TTY device. If less than three values are provided, the list will be padded with the last value. Specifying more than three values will create additional file descriptors (4, 5, etc.).",
"default": []
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index 93446c051eb5..45caa1a81059 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -16,15 +16,19 @@
#include "lldb/API/SBStream.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/File.h"
+#include "lldb/Host/FileSystem.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
#include "lldb/Host/MemoryMonitor.h"
#include "lldb/Host/Socket.h"
+#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/Status.h"
#include "lldb/Utility/UriParser.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ScopeExit.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/Arg.h"
@@ -42,8 +46,10 @@
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <condition_variable>
+#include <cstddef>
#include <cstdio>
#include <cstdlib>
+#include <exception>
#include <fcntl.h>
#include <map>
#include <memory>
@@ -69,6 +75,7 @@ typedef int socklen_t;
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <termios.h>
#include <unistd.h>
#endif
@@ -143,6 +150,74 @@ static void PrintVersion() {
llvm::outs() << "liblldb: " << lldb::SBDebugger::GetVersionString() << '\n';
}
+#if not defined(_WIN32)
+struct FDGroup {
+ int GetFlags() const {
+ if (read && write)
+ return O_NOCTTY | O_CREAT | O_RDWR;
+ if (read)
+ return O_NOCTTY | O_RDONLY;
+ return O_NOCTTY | O_CREAT | O_WRONLY | O_TRUNC;
+ }
+
+ std::vector<int> fds;
+ bool read = false;
+ bool write = false;
+};
+
+static llvm::Error RedirectToFile(const FDGroup &fdg, llvm::StringRef file) {
+ if (!fdg.read && !fdg.write)
+ return llvm::Error::success();
+ int target_fd = lldb_private::FileSystem::Instance().Open(
+ file.str().c_str(), fdg.GetFlags(), 0666);
+ if (target_fd == -1)
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ for (int fd : fdg.fds) {
+ if (target_fd == fd)
+ continue;
+ if (::dup2(target_fd, fd) == -1)
+ return llvm::errorCodeToError(
+ std::error_code(errno, std::generic_category()));
+ }
+ ::close(target_fd);
+ return llvm::Error::success();
+}
+
+static llvm::Error
+SetupIORedirection(const llvm::SmallVectorImpl<llvm::StringRef> &files) {
+ llvm::SmallDenseMap<llvm::StringRef, FDGroup> groups;
+ for (size_t i = 0; i < files.size(); i++) {
+ if (files[i].empty())
+ continue;
+ auto group = groups.find(files[i]);
+ if (group == groups.end())
+ group = groups.insert({files[i], {{static_cast<int>(i)}}}).first;
+ else
+ group->second.fds.push_back(i);
+ switch (i) {
+ case 0:
+ group->second.read = true;
+ break;
+ case 1:
+ case 2:
+ group->second.write = true;
+ break;
+ default:
+ group->second.read = true;
+ group->second.write = true;
+ break;
+ }
+ }
+ for (const auto &[file, group] : groups) {
+ if (llvm::Error err = RedirectToFile(group, file))
+ return llvm::createStringError(
+ llvm::formatv("{0}: {1}", file, llvm::toString(std::move(err))));
+ }
+ return llvm::Error::success();
+}
+#endif
+
// If --launch-target is provided, this instance of lldb-dap becomes a
// runInTerminal launcher. It will ultimately launch the program specified in
// the --launch-target argument, which is the original program the user wanted
@@ -165,6 +240,7 @@ static void PrintVersion() {
static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
llvm::StringRef comm_file,
lldb::pid_t debugger_pid,
+ llvm::StringRef stdio,
char *argv[]) {
#if defined(_WIN32)
return llvm::createStringError(
@@ -179,6 +255,25 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
(void)prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
#endif
+ lldb_private::FileSystem::Initialize();
+ if (!stdio.empty()) {
+ llvm::SmallVector<llvm::StringRef, 3> files;
+ stdio.split(files, ':');
+ while (files.size() < 3)
+ files.push_back(files.back());
+ if (llvm::Error err = SetupIORedirection(files))
+ return err;
+ } else if ((isatty(STDIN_FILENO) != 0) &&
+ llvm::StringRef(getenv("TERM")).starts_with_insensitive("xterm")) {
+ // Clear the screen.
+ llvm::outs() << ANSI_CSI_RESET_CURSOR ANSI_CSI_ERASE_VIEWPORT
+ ANSI_CSI_ERASE_SCROLLBACK;
+ // VS Code will reuse the same terminal for the same debug configuration
+ // between runs. Clear the input buffer prior to starting the new process so
+ // prior input is not carried forward to the new debug session.
+ tcflush(STDIN_FILENO, TCIFLUSH);
+ }
+
RunInTerminalLauncherCommChannel comm_channel(comm_file);
if (llvm::Error err = comm_channel.NotifyPid())
return err;
@@ -484,9 +579,10 @@ int main(int argc, char *argv[]) {
break;
}
}
+ llvm::StringRef stdio = input_args.getLastArgValue(OPT_stdio);
if (llvm::Error err =
LaunchRunInTerminalTarget(*target_arg, comm_file->getValue(), pid,
- argv + target_args_pos)) {
+ stdio, argv + target_args_pos)) {
llvm::errs() << llvm::toString(std::move(err)) << '\n';
return EXIT_FAILURE;
}
diff --git a/lldb/tools/lldb-mcp/lldb-mcp.cpp b/lldb/tools/lldb-mcp/lldb-mcp.cpp
index 68e987237cc6..3fc362232ba6 100644
--- a/lldb/tools/lldb-mcp/lldb-mcp.cpp
+++ b/lldb/tools/lldb-mcp/lldb-mcp.cpp
@@ -85,6 +85,8 @@ FileSpec driverPath() {
llvm::Error launch() {
FileSpec lldb_exec = driverPath();
lldb_private::ProcessLaunchInfo info;
+ info.SetMonitorProcessCallback(
+ &lldb_private::ProcessLaunchInfo::NoOpMonitorCallback);
info.SetExecutableFile(lldb_exec,
/*add_exe_file_as_first_arg=*/true);
info.GetArguments().AppendArgument("-O");
diff --git a/lldb/unittests/API/CMakeLists.txt b/lldb/unittests/API/CMakeLists.txt
index 06ac49244176..b86054fb353f 100644
--- a/lldb/unittests/API/CMakeLists.txt
+++ b/lldb/unittests/API/CMakeLists.txt
@@ -2,13 +2,17 @@ add_lldb_unittest(APITests
SBCommandInterpreterTest.cpp
SBLineEntryTest.cpp
SBMutexTest.cpp
+ SBBreakpointClearConditionTest.cpp
+
+ SBAPITEST
LINK_LIBS
liblldb
)
# Build with -Wdocumentation. This relies on the tests including all the API
-# headers through API/LLDB.h.
+# headers through API/LLDB.h. It also means that the API tests cannot include
+# private headers.
check_cxx_compiler_flag("-Wdocumentation"
CXX_SUPPORTS_DOCUMENTATION)
if (CXX_SUPPORTS_DOCUMENTATION)
diff --git a/lldb/unittests/API/SBBreakpointClearConditionTest.cpp b/lldb/unittests/API/SBBreakpointClearConditionTest.cpp
new file mode 100644
index 000000000000..993f7f90d97c
--- /dev/null
+++ b/lldb/unittests/API/SBBreakpointClearConditionTest.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// Use the umbrella header for -Wdocumentation.
+#include "lldb/API/LLDB.h"
+
+#include "TestingSupport/SubsystemRAII.h"
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBTarget.h"
+#include "gtest/gtest.h"
+#include <memory>
+#include <mutex>
+
+using namespace lldb_private;
+using namespace lldb;
+
+class BreakpointClearConditionTest : public ::testing::Test {
+public:
+ void SetUp() override {
+ m_sb_debugger = SBDebugger::Create(/*source_init_files=*/false);
+ };
+
+ void TearDown() override { SBDebugger::Destroy(m_sb_debugger); }
+ SBDebugger m_sb_debugger;
+ SubsystemRAII<lldb::SBDebugger> subsystems;
+};
+
+template <typename T> void test_condition(T sb_object) {
+ const char *in_cond_str = "Here is a condition";
+ sb_object.SetCondition(in_cond_str);
+ // Make sure we set the condition correctly:
+ const char *out_cond_str = sb_object.GetCondition();
+ EXPECT_STREQ(in_cond_str, out_cond_str);
+ // Now unset it by passing in nullptr and make sure that works:
+ const char *empty_tokens[2] = {nullptr, ""};
+ for (auto token : empty_tokens) {
+ sb_object.SetCondition(token);
+ out_cond_str = sb_object.GetCondition();
+ // And make sure an unset condition returns nullptr:
+ EXPECT_EQ(nullptr, out_cond_str);
+ }
+}
+
+TEST_F(BreakpointClearConditionTest, BreakpointClearConditionTest) {
+ // Create target
+ SBTarget sb_target;
+ SBError error;
+ sb_target =
+ m_sb_debugger.CreateTarget("", "x86_64-apple-macosx-", "remote-macosx",
+ /*add_dependent=*/false, error);
+
+ EXPECT_EQ(sb_target.IsValid(), true);
+
+ // Create breakpoint
+ SBBreakpoint sb_breakpoint = sb_target.BreakpointCreateByAddress(0xDEADBEEF);
+ test_condition(sb_breakpoint);
+
+ // Address breakpoints always have one location, so we can also use this
+ // to test the location:
+ SBBreakpointLocation sb_loc = sb_breakpoint.GetLocationAtIndex(0);
+ EXPECT_EQ(sb_loc.IsValid(), true);
+ test_condition(sb_loc);
+}
diff --git a/lldb/unittests/Breakpoint/CMakeLists.txt b/lldb/unittests/Breakpoint/CMakeLists.txt
index 3c234a4fea29..dec2265a725a 100644
--- a/lldb/unittests/Breakpoint/CMakeLists.txt
+++ b/lldb/unittests/Breakpoint/CMakeLists.txt
@@ -1,10 +1,10 @@
-add_lldb_unittest(LLDBBreakpointTests
- BreakpointIDTest.cpp
- WatchpointAlgorithmsTests.cpp
-
- LINK_COMPONENTS
- Support
- LINK_LIBS
- lldbBreakpoint
- lldbCore
- )
+add_lldb_unittest(LLDBBreakpointTests
+ BreakpointIDTest.cpp
+ WatchpointAlgorithmsTests.cpp
+
+ LINK_COMPONENTS
+ Support
+ LINK_LIBS
+ lldbBreakpoint
+ lldbCore
+ )
diff --git a/lldb/unittests/CMakeLists.txt b/lldb/unittests/CMakeLists.txt
index 4c5267ae25b7..194dd425430e 100644
--- a/lldb/unittests/CMakeLists.txt
+++ b/lldb/unittests/CMakeLists.txt
@@ -12,7 +12,7 @@ endif()
function(add_lldb_unittest test_name)
cmake_parse_arguments(ARG
- ""
+ "SBAPITEST"
""
"LINK_LIBS;LINK_COMPONENTS"
${ARGN})
@@ -21,6 +21,10 @@ function(add_lldb_unittest test_name)
message(FATAL_ERROR "Unit test name must end with 'Tests' for lit to find it.")
endif()
+ if ("liblldb" IN_LIST ARG_LINK_LIBS AND NOT ARG_SBAPITEST)
+ message(FATAL_ERROR "The ${test_name} are not allowed to link liblldb.")
+ endif()
+
list(APPEND LLVM_LINK_COMPONENTS ${ARG_LINK_COMPONENTS})
add_unittest(LLDBUnitTests
diff --git a/lldb/unittests/DAP/CMakeLists.txt b/lldb/unittests/DAP/CMakeLists.txt
index 716159b45423..a08414c30e6c 100644
--- a/lldb/unittests/DAP/CMakeLists.txt
+++ b/lldb/unittests/DAP/CMakeLists.txt
@@ -12,6 +12,8 @@ add_lldb_unittest(DAPTests
TestBase.cpp
VariablesTest.cpp
+ SBAPITEST
+
LINK_COMPONENTS
Support
LINK_LIBS
diff --git a/lldb/unittests/DAP/DAPTest.cpp b/lldb/unittests/DAP/DAPTest.cpp
index 2090fe6896d6..4fd6cd546e6f 100644
--- a/lldb/unittests/DAP/DAPTest.cpp
+++ b/lldb/unittests/DAP/DAPTest.cpp
@@ -9,13 +9,10 @@
#include "DAP.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
-#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>
-using namespace llvm;
-using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;
@@ -24,18 +21,7 @@ using namespace testing;
class DAPTest : public TransportBase {};
TEST_F(DAPTest, SendProtocolMessages) {
- DAP dap{
- /*log=*/nullptr,
- /*default_repl_mode=*/ReplMode::Auto,
- /*pre_init_commands=*/{},
- /*no_lldbinit=*/false,
- /*client_name=*/"test_client",
- /*transport=*/*transport,
- /*loop=*/loop,
- };
- dap.Send(Event{/*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());
+ dap->Send(Event{/*event=*/"my-event", /*body=*/std::nullopt});
+ EXPECT_CALL(client, Received(IsEvent("my-event")));
+ Run();
}
diff --git a/lldb/unittests/DAP/Handler/DisconnectTest.cpp b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
index c6ff1f90b01d..88d6e9a69eca 100644
--- a/lldb/unittests/DAP/Handler/DisconnectTest.cpp
+++ b/lldb/unittests/DAP/Handler/DisconnectTest.cpp
@@ -31,7 +31,7 @@ TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminated) {
DisconnectRequestHandler handler(*dap);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
EXPECT_CALL(client, Received(IsEvent("terminated", _)));
- RunOnce();
+ Run();
}
TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminateCommands) {
@@ -53,5 +53,5 @@ TEST_F(DisconnectRequestHandlerTest, DisconnectTriggersTerminateCommands) {
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();
+ Run();
}
diff --git a/lldb/unittests/DAP/ProtocolTypesTest.cpp b/lldb/unittests/DAP/ProtocolTypesTest.cpp
index 0989a5becc8f..a5ae856a185b 100644
--- a/lldb/unittests/DAP/ProtocolTypesTest.cpp
+++ b/lldb/unittests/DAP/ProtocolTypesTest.cpp
@@ -1101,3 +1101,28 @@ TEST(ProtocolTypesTest, MemoryEventBody) {
})";
EXPECT_EQ(json, pp(body));
}
+
+TEST(ProtocolTypesTest, DataBreakpointInfoArguments) {
+ llvm::Expected<DataBreakpointInfoArguments> expected =
+ parse<DataBreakpointInfoArguments>(R"({
+ "name": "data",
+ "variablesReference": 8,
+ "frameId": 9,
+ "bytes": 10,
+ "asAddress": false,
+ "mode": "source"
+ })");
+ ASSERT_THAT_EXPECTED(expected, llvm::Succeeded());
+ EXPECT_EQ(expected->name, "data");
+ EXPECT_EQ(expected->variablesReference, 8);
+ EXPECT_EQ(expected->frameId, 9u);
+ EXPECT_EQ(expected->bytes, 10);
+ EXPECT_EQ(expected->asAddress, false);
+ EXPECT_EQ(expected->mode, "source");
+
+ // Check required keys.
+ EXPECT_THAT_EXPECTED(parse<DataBreakpointInfoArguments>(R"({})"),
+ FailedWithMessage("missing value at (root).name"));
+ EXPECT_THAT_EXPECTED(parse<DataBreakpointInfoArguments>(R"({"name":"data"})"),
+ llvm::Succeeded());
+}
diff --git a/lldb/unittests/DAP/TestBase.cpp b/lldb/unittests/DAP/TestBase.cpp
index ba7baf210379..3721e09d8b69 100644
--- a/lldb/unittests/DAP/TestBase.cpp
+++ b/lldb/unittests/DAP/TestBase.cpp
@@ -32,23 +32,9 @@ using lldb_private::FileSystem;
using lldb_private::MainLoop;
using lldb_private::Pipe;
-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 TransportBase::SetUp() {
+ std::tie(to_client, to_server) = TestDAPTransport::createPair();
-void DAPTestBase::SetUp() {
- TransportBase::SetUp();
std::error_code EC;
log = std::make_unique<Log>("-", EC);
dap = std::make_unique<DAP>(
@@ -57,16 +43,30 @@ void DAPTestBase::SetUp() {
/*pre_init_commands=*/std::vector<std::string>(),
/*no_lldbinit=*/false,
/*client_name=*/"test_client",
- /*transport=*/*transport, /*loop=*/loop);
+ /*transport=*/*to_client, /*loop=*/loop);
+
+ auto server_handle = to_server->RegisterMessageHandler(loop, *dap.get());
+ EXPECT_THAT_EXPECTED(server_handle, Succeeded());
+ handles[0] = std::move(*server_handle);
+
+ auto client_handle = to_client->RegisterMessageHandler(loop, client);
+ EXPECT_THAT_EXPECTED(client_handle, Succeeded());
+ handles[1] = std::move(*client_handle);
}
+void TransportBase::Run() {
+ loop.AddPendingCallback(
+ [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); });
+ EXPECT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());
+}
+
+void DAPTestBase::SetUp() { TransportBase::SetUp(); }
+
void DAPTestBase::TearDown() {
- if (core) {
+ if (core)
ASSERT_THAT_ERROR(core->discard(), Succeeded());
- }
- if (binary) {
+ if (binary)
ASSERT_THAT_ERROR(binary->discard(), Succeeded());
- }
}
void DAPTestBase::SetUpTestSuite() {
diff --git a/lldb/unittests/DAP/TestBase.h b/lldb/unittests/DAP/TestBase.h
index c19eead4e37e..c32f3a769c73 100644
--- a/lldb/unittests/DAP/TestBase.h
+++ b/lldb/unittests/DAP/TestBase.h
@@ -7,73 +7,48 @@
//===----------------------------------------------------------------------===//
#include "DAP.h"
+#include "DAPLog.h"
#include "Protocol/ProtocolBase.h"
#include "TestingSupport/Host/JSONTransportTestUtilities.h"
#include "TestingSupport/SubsystemRAII.h"
+#include "Transport.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>
+#include <optional>
+
+/// Helpers for gtest printing.
+namespace lldb_dap::protocol {
+
+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 Event &evt, std::ostream *os) {
+ *os << llvm::formatv("{0}", toJSON(evt)).str();
+}
+
+inline void PrintTo(const Message &message, std::ostream *os) {
+ return std::visit([os](auto &&message) { return PrintTo(message, os); },
+ message);
+}
+
+} // namespace lldb_dap::protocol
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;
-};
+using TestDAPTransport = TestTransport<lldb_dap::ProtocolDescriptor>;
/// A base class for tests that need transport configured for communicating DAP
/// messages.
@@ -82,22 +57,36 @@ protected:
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 {
- transport = std::make_unique<TestTransport>(loop, client);
- }
+ lldb_private::MainLoop::ReadHandleUP handles[2];
+
+ std::unique_ptr<lldb_dap::Log> log;
+
+ std::unique_ptr<TestDAPTransport> to_client;
+ MockMessageHandler<lldb_dap::ProtocolDescriptor> client;
+
+ std::unique_ptr<TestDAPTransport> to_server;
+ std::unique_ptr<lldb_dap::DAP> dap;
+
+ void SetUp() override;
+
+ void Run();
};
/// A matcher for a DAP event.
-template <typename M1, typename M2>
+template <typename EventMatcher, typename BodyMatcher>
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));
+IsEvent(const EventMatcher &event_matcher, const BodyMatcher &body_matcher) {
+ return testing::AllOf(
+ testing::Field(&lldb_dap::protocol::Event::event, event_matcher),
+ testing::Field(&lldb_dap::protocol::Event::body, body_matcher));
+}
+
+template <typename EventMatcher>
+inline testing::Matcher<const lldb_dap::protocol::Event &>
+IsEvent(const EventMatcher &event_matcher) {
+ return testing::AllOf(
+ testing::Field(&lldb_dap::protocol::Event::event, event_matcher),
+ testing::Field(&lldb_dap::protocol::Event::body, std::nullopt));
}
/// Matches an "output" event.
@@ -110,8 +99,6 @@ inline auto Output(llvm::StringRef o, llvm::StringRef cat = "console") {
/// 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;
@@ -126,12 +113,6 @@ protected:
bool GetDebuggerSupportsTarget(llvm::StringRef platform);
void CreateDebugger();
void LoadCore();
-
- 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/Host/JSONTransportTest.cpp b/lldb/unittests/Host/JSONTransportTest.cpp
index 3a36bf21f07f..54f1372ca0ff 100644
--- a/lldb/unittests/Host/JSONTransportTest.cpp
+++ b/lldb/unittests/Host/JSONTransportTest.cpp
@@ -9,6 +9,7 @@
#include "lldb/Host/JSONTransport.h"
#include "TestingSupport/Host/JSONTransportTestUtilities.h"
#include "TestingSupport/Host/PipeTestUtilities.h"
+#include "TestingSupport/SubsystemRAII.h"
#include "lldb/Host/File.h"
#include "lldb/Host/MainLoop.h"
#include "lldb/Host/MainLoopBase.h"
@@ -25,79 +26,108 @@
#include <chrono>
#include <cstddef>
#include <memory>
+#include <optional>
#include <string>
+#include <system_error>
using namespace llvm;
using namespace lldb_private;
+using namespace lldb_private::transport;
using testing::_;
using testing::HasSubstr;
using testing::InSequence;
+using testing::Ref;
+
+namespace llvm::json {
+static bool fromJSON(const Value &V, Value &T, Path P) {
+ T = V;
+ return true;
+}
+} // namespace llvm::json
namespace {
namespace test_protocol {
-struct Req {
+struct Request {
+ int id = 0;
std::string name;
+ std::optional<json::Value> params;
};
-json::Value toJSON(const Req &T) { return json::Object{{"req", T.name}}; }
-bool fromJSON(const json::Value &V, Req &T, json::Path P) {
+json::Value toJSON(const Request &T) {
+ return json::Object{{"name", T.name}, {"id", T.id}, {"params", T.params}};
+}
+bool fromJSON(const json::Value &V, Request &T, json::Path P) {
json::ObjectMapper O(V, P);
- return O && O.map("req", T.name);
+ return O && O.map("name", T.name) && O.map("id", T.id) &&
+ O.map("params", T.params);
}
-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) {
+bool operator==(const Request &a, const Request &b) {
+ return a.name == b.name && a.id == b.id && a.params == b.params;
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Request &V) {
OS << toJSON(V);
return OS;
}
-void PrintTo(const Req &message, std::ostream *os) {
+void PrintTo(const Request &message, std::ostream *os) {
std::string O;
llvm::raw_string_ostream OS(O);
OS << message;
*os << O;
}
-struct Resp {
- std::string name;
+struct Response {
+ int id = 0;
+ int errorCode = 0;
+ std::optional<json::Value> result;
};
-json::Value toJSON(const Resp &T) { return json::Object{{"resp", T.name}}; }
-bool fromJSON(const json::Value &V, Resp &T, json::Path P) {
+json::Value toJSON(const Response &T) {
+ return json::Object{
+ {"id", T.id}, {"errorCode", T.errorCode}, {"result", T.result}};
+}
+bool fromJSON(const json::Value &V, Response &T, json::Path P) {
json::ObjectMapper O(V, P);
- return O && O.map("resp", T.name);
+ return O && O.map("id", T.id) && O.mapOptional("errorCode", T.errorCode) &&
+ O.map("result", T.result);
}
-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) {
+bool operator==(const Response &a, const Response &b) {
+ return a.id == b.id && a.errorCode == b.errorCode && a.result == b.result;
+}
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Response &V) {
OS << toJSON(V);
return OS;
}
-void PrintTo(const Resp &message, std::ostream *os) {
+void PrintTo(const Response &message, std::ostream *os) {
std::string O;
llvm::raw_string_ostream OS(O);
OS << message;
*os << O;
}
-struct Evt {
+struct Event {
std::string name;
+ std::optional<json::Value> params;
};
-json::Value toJSON(const Evt &T) { return json::Object{{"evt", T.name}}; }
-bool fromJSON(const json::Value &V, Evt &T, json::Path P) {
+json::Value toJSON(const Event &T) {
+ return json::Object{{"name", T.name}, {"params", T.params}};
+}
+bool fromJSON(const json::Value &V, Event &T, json::Path P) {
json::ObjectMapper O(V, P);
- return O && O.map("evt", T.name);
+ return O && O.map("name", T.name) && O.map("params", T.params);
}
-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) {
+bool operator==(const Event &a, const Event &b) { return a.name == b.name; }
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Event &V) {
OS << toJSON(V);
return OS;
}
-void PrintTo(const Evt &message, std::ostream *os) {
+void PrintTo(const Event &message, std::ostream *os) {
std::string O;
llvm::raw_string_ostream OS(O);
OS << message;
*os << O;
}
-using Message = std::variant<Req, Resp, Evt>;
+using Message = std::variant<Request, Response, Event>;
json::Value toJSON(const Message &msg) {
return std::visit([](const auto &msg) { return toJSON(msg); }, msg);
}
@@ -107,41 +137,114 @@ bool fromJSON(const json::Value &V, Message &msg, json::Path P) {
P.report("expected object");
return false;
}
- if (O->get("req")) {
- Req R;
- if (!fromJSON(V, R, P))
+
+ if (O->find("id") == O->end()) {
+ Event E;
+ if (!fromJSON(V, E, P))
return false;
- msg = std::move(R);
+ msg = std::move(E);
return true;
}
- if (O->get("resp")) {
- Resp R;
+
+ if (O->get("name")) {
+ Request 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;
+ Response R;
+ if (!fromJSON(V, R, P))
+ return false;
+
+ msg = std::move(R);
+ return true;
}
-} // namespace test_protocol
+struct MyFnParams {
+ int a = 0;
+ int b = 0;
+};
+json::Value toJSON(const MyFnParams &T) {
+ return json::Object{{"a", T.a}, {"b", T.b}};
+}
+bool fromJSON(const json::Value &V, MyFnParams &T, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("a", T.a) && O.map("b", T.b);
+}
+
+struct MyFnResult {
+ int c = 0;
+};
+json::Value toJSON(const MyFnResult &T) { return json::Object{{"c", T.c}}; }
+bool fromJSON(const json::Value &V, MyFnResult &T, json::Path P) {
+ json::ObjectMapper O(V, P);
+ return O && O.map("c", T.c);
+}
+
+struct ProtoDesc {
+ using Id = int;
+ using Req = Request;
+ using Resp = Response;
+ using Evt = Event;
+
+ static inline Id InitialId() { return 0; }
+ static inline Req Make(Id id, llvm::StringRef method,
+ std::optional<llvm::json::Value> params) {
+ return Req{id, method.str(), params};
+ }
+ static inline Evt Make(llvm::StringRef method,
+ std::optional<llvm::json::Value> params) {
+ return Evt{method.str(), params};
+ }
+ static inline Resp Make(Req req, llvm::Error error) {
+ Resp resp;
+ resp.id = req.id;
+ llvm::handleAllErrors(
+ std::move(error), [&](const llvm::ErrorInfoBase &err) {
+ std::error_code cerr = err.convertToErrorCode();
+ resp.errorCode =
+ cerr == llvm::inconvertibleErrorCode() ? 1 : cerr.value();
+ resp.result = err.message();
+ });
+ return resp;
+ }
+ static inline Resp Make(Req req, std::optional<llvm::json::Value> result) {
+ return Resp{req.id, 0, std::move(result)};
+ }
+ static inline Id KeyFor(Resp r) { return r.id; }
+ static inline std::string KeyFor(Req r) { return r.name; }
+ static inline std::string KeyFor(Evt e) { return e.name; }
+ static inline std::optional<llvm::json::Value> Extract(Req r) {
+ return r.params;
+ }
+ static inline llvm::Expected<llvm::json::Value> Extract(Resp r) {
+ if (r.errorCode != 0)
+ return llvm::createStringError(
+ std::error_code(r.errorCode, std::generic_category()),
+ r.result && r.result->getAsString() ? *r.result->getAsString()
+ : "no-message");
+ return r.result;
+ }
+ static inline std::optional<llvm::json::Value> Extract(Evt e) {
+ return e.params;
+ }
+};
+
+using Transport = TestTransport<ProtoDesc>;
+using Binder = lldb_private::transport::Binder<ProtoDesc>;
+using MessageHandler = MockMessageHandler<ProtoDesc>;
-template <typename T, typename Req, typename Resp, typename Evt>
-class JSONTransportTest : public PipePairTest {
+} // namespace test_protocol
+template <typename T> class JSONTransportTest : public PipePairTest {
protected:
- MockMessageHandler<Req, Resp, Evt> message_handler;
+ SubsystemRAII<FileSystem> subsystems;
+
+ test_protocol::MessageHandler message_handler;
std::unique_ptr<T> transport;
MainLoop loop;
@@ -191,8 +294,7 @@ protected:
};
class TestHTTPDelimitedJSONTransport final
- : public HTTPDelimitedJSONTransport<test_protocol::Req, test_protocol::Resp,
- test_protocol::Evt> {
+ : public HTTPDelimitedJSONTransport<test_protocol::ProtoDesc> {
public:
using HTTPDelimitedJSONTransport::HTTPDelimitedJSONTransport;
@@ -204,9 +306,7 @@ public:
};
class HTTPDelimitedJSONTransportTest
- : public JSONTransportTest<TestHTTPDelimitedJSONTransport,
- test_protocol::Req, test_protocol::Resp,
- test_protocol::Evt> {
+ : public JSONTransportTest<TestHTTPDelimitedJSONTransport> {
public:
using JSONTransportTest::JSONTransportTest;
@@ -222,8 +322,7 @@ public:
};
class TestJSONRPCTransport final
- : public JSONRPCTransport<test_protocol::Req, test_protocol::Resp,
- test_protocol::Evt> {
+ : public JSONRPCTransport<test_protocol::ProtoDesc> {
public:
using JSONRPCTransport::JSONRPCTransport;
@@ -234,9 +333,7 @@ public:
std::vector<std::string> log_messages;
};
-class JSONRPCTransportTest
- : public JSONTransportTest<TestJSONRPCTransport, test_protocol::Req,
- test_protocol::Resp, test_protocol::Evt> {
+class JSONRPCTransportTest : public JSONTransportTest<TestJSONRPCTransport> {
public:
using JSONTransportTest::JSONTransportTest;
@@ -248,6 +345,33 @@ public:
}
};
+class TransportBinderTest : public testing::Test {
+protected:
+ SubsystemRAII<FileSystem> subsystems;
+
+ std::unique_ptr<test_protocol::Transport> to_remote;
+ std::unique_ptr<test_protocol::Transport> from_remote;
+ std::unique_ptr<test_protocol::Binder> binder;
+ test_protocol::MessageHandler remote;
+ MainLoop loop;
+
+ void SetUp() override {
+ std::tie(to_remote, from_remote) = test_protocol::Transport::createPair();
+ binder = std::make_unique<test_protocol::Binder>(*to_remote);
+
+ auto binder_handle = to_remote->RegisterMessageHandler(loop, remote);
+ EXPECT_THAT_EXPECTED(binder_handle, Succeeded());
+
+ auto remote_handle = from_remote->RegisterMessageHandler(loop, *binder);
+ EXPECT_THAT_EXPECTED(remote_handle, Succeeded());
+ }
+
+ void Run() {
+ loop.AddPendingCallback([](auto &loop) { loop.RequestTermination(); });
+ EXPECT_THAT_ERROR(loop.Run().takeError(), Succeeded());
+ }
+};
+
} // namespace
// Failing on Windows, see https://github.com/llvm/llvm-project/issues/153446.
@@ -269,35 +393,46 @@ TEST_F(HTTPDelimitedJSONTransportTest, MalformedRequests) {
}
TEST_F(HTTPDelimitedJSONTransportTest, Read) {
- Write(Req{"foo"});
- EXPECT_CALL(message_handler, Received(Req{"foo"}));
+ Write(Request{6, "foo", std::nullopt});
+ EXPECT_CALL(message_handler, Received(Request{6, "foo", std::nullopt}));
ASSERT_THAT_ERROR(Run(), Succeeded());
}
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"}));
+ Write(
+ Message{
+ Request{6, "one", std::nullopt},
+ },
+ Message{
+ test_protocol::Event{"two", std::nullopt},
+ },
+ Message{
+ Response{2, 0, std::nullopt},
+ });
+ EXPECT_CALL(message_handler, Received(Request{6, "one", std::nullopt}));
+ EXPECT_CALL(message_handler,
+ Received(test_protocol::Event{"two", std::nullopt}));
+ EXPECT_CALL(message_handler, Received(Response{2, 0, std::nullopt}));
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}));
+ HTTPDelimitedJSONTransport<test_protocol::ProtoDesc>::kReadBufferSize * 2,
+ 'x');
+ Write(Request{5, long_str, std::nullopt});
+ EXPECT_CALL(message_handler, Received(Request{5, long_str, std::nullopt}));
ASSERT_THAT_ERROR(Run(), Succeeded());
}
TEST_F(HTTPDelimitedJSONTransportTest, ReadPartialMessage) {
- std::string message = Encode(Req{"foo"});
+ std::string message = Encode(Request{5, "foo", std::nullopt});
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"}));
+ EXPECT_CALL(message_handler, Received(Request{5, "foo", std::nullopt}));
ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
loop.AddPendingCallback(
@@ -309,12 +444,12 @@ TEST_F(HTTPDelimitedJSONTransportTest, ReadPartialMessage) {
}
TEST_F(HTTPDelimitedJSONTransportTest, ReadWithZeroByteWrites) {
- std::string message = Encode(Req{"foo"});
+ std::string message = Encode(Request{6, "foo", std::nullopt});
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"}));
+ EXPECT_CALL(message_handler, Received(Request{6, "foo", std::nullopt}));
ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
@@ -366,20 +501,23 @@ TEST_F(HTTPDelimitedJSONTransportTest, InvalidTransport) {
}
TEST_F(HTTPDelimitedJSONTransportTest, Write) {
- ASSERT_THAT_ERROR(transport->Send(Req{"foo"}), Succeeded());
- ASSERT_THAT_ERROR(transport->Send(Resp{"bar"}), Succeeded());
- ASSERT_THAT_ERROR(transport->Send(Evt{"baz"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Request{7, "foo", std::nullopt}),
+ Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Response{5, 0, "bar"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(test_protocol::Event{"baz", std::nullopt}),
+ 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"({"req":"foo"})"
- "Content-Length: 14\r\n\r\n"
- R"({"resp":"bar"})"
- "Content-Length: 13\r\n\r\n"
- R"({"evt":"baz"})"));
+ ASSERT_EQ(StringRef(buf, *bytes_read),
+ StringRef("Content-Length: 35\r\n\r\n"
+ R"({"id":7,"name":"foo","params":null})"
+ "Content-Length: 37\r\n\r\n"
+ R"({"errorCode":0,"id":5,"result":"bar"})"
+ "Content-Length: 28\r\n\r\n"
+ R"({"name":"baz","params":null})"));
}
TEST_F(JSONRPCTransportTest, MalformedRequests) {
@@ -395,37 +533,40 @@ TEST_F(JSONRPCTransportTest, MalformedRequests) {
}
TEST_F(JSONRPCTransportTest, Read) {
- Write(Message{Req{"foo"}});
- EXPECT_CALL(message_handler, Received(Req{"foo"}));
+ Write(Message{Request{1, "foo", std::nullopt}});
+ EXPECT_CALL(message_handler, Received(Request{1, "foo", std::nullopt}));
ASSERT_THAT_ERROR(Run(), Succeeded());
}
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"}));
+ Write(Message{Request{1, "one", std::nullopt}},
+ Message{test_protocol::Event{"two", std::nullopt}},
+ Message{Response{3, 0, "three"}});
+ EXPECT_CALL(message_handler, Received(Request{1, "one", std::nullopt}));
+ EXPECT_CALL(message_handler,
+ Received(test_protocol::Event{"two", std::nullopt}));
+ EXPECT_CALL(message_handler, Received(Response{3, 0, "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(IOTransport<Req, Resp, Evt>::kReadBufferSize * 2, 'x');
- Write(Req{long_str});
- EXPECT_CALL(message_handler, Received(Req{long_str}));
+ std::string long_str = std::string(
+ IOTransport<test_protocol::ProtoDesc>::kReadBufferSize * 2, 'x');
+ Write(Request{42, long_str, std::nullopt});
+ EXPECT_CALL(message_handler, Received(Request{42, long_str, std::nullopt}));
ASSERT_THAT_ERROR(Run(), Succeeded());
}
TEST_F(JSONRPCTransportTest, ReadPartialMessage) {
- std::string message = R"({"req": "foo"})"
+ std::string message = R"({"id":42,"name":"foo","params":null})"
"\n";
std::string part1 = message.substr(0, 7);
std::string part2 = message.substr(7);
- EXPECT_CALL(message_handler, Received(Req{"foo"}));
+ EXPECT_CALL(message_handler, Received(Request{42, "foo", std::nullopt}));
ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());
loop.AddPendingCallback(
@@ -455,20 +596,23 @@ TEST_F(JSONRPCTransportTest, ReaderWithUnhandledData) {
}
TEST_F(JSONRPCTransportTest, Write) {
- ASSERT_THAT_ERROR(transport->Send(Req{"foo"}), Succeeded());
- ASSERT_THAT_ERROR(transport->Send(Resp{"bar"}), Succeeded());
- ASSERT_THAT_ERROR(transport->Send(Evt{"baz"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Request{11, "foo", std::nullopt}),
+ Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(Response{14, 0, "bar"}), Succeeded());
+ ASSERT_THAT_ERROR(transport->Send(test_protocol::Event{"baz", std::nullopt}),
+ 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"({"req":"foo"})"
- "\n"
- R"({"resp":"bar"})"
- "\n"
- R"({"evt":"baz"})"
- "\n"));
+ ASSERT_EQ(StringRef(buf, *bytes_read),
+ StringRef(R"({"id":11,"name":"foo","params":null})"
+ "\n"
+ R"({"errorCode":0,"id":14,"result":"bar"})"
+ "\n"
+ R"({"name":"baz","params":null})"
+ "\n"));
}
TEST_F(JSONRPCTransportTest, InvalidTransport) {
@@ -477,4 +621,189 @@ TEST_F(JSONRPCTransportTest, InvalidTransport) {
FailedWithMessage("IO object is not valid."));
}
+// Out-bound binding request handler.
+TEST_F(TransportBinderTest, OutBoundRequests) {
+ OutgoingRequest<MyFnResult, MyFnParams> addFn =
+ binder->Bind<MyFnResult, MyFnParams>("add");
+ bool replied = false;
+ addFn(MyFnParams{1, 2}, [&](Expected<MyFnResult> result) {
+ EXPECT_THAT_EXPECTED(result, Succeeded());
+ EXPECT_EQ(result->c, 3);
+ replied = true;
+ });
+ EXPECT_CALL(remote, Received(Request{1, "add", MyFnParams{1, 2}}));
+ EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, toJSON(MyFnResult{3})}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(replied);
+}
+
+TEST_F(TransportBinderTest, OutBoundRequestsVoidParams) {
+ OutgoingRequest<MyFnResult, void> voidParamFn =
+ binder->Bind<MyFnResult, void>("voidParam");
+ bool replied = false;
+ voidParamFn([&](Expected<MyFnResult> result) {
+ EXPECT_THAT_EXPECTED(result, Succeeded());
+ EXPECT_EQ(result->c, 3);
+ replied = true;
+ });
+ EXPECT_CALL(remote, Received(Request{1, "voidParam", std::nullopt}));
+ EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, toJSON(MyFnResult{3})}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(replied);
+}
+
+TEST_F(TransportBinderTest, OutBoundRequestsVoidResult) {
+ OutgoingRequest<void, MyFnParams> voidResultFn =
+ binder->Bind<void, MyFnParams>("voidResult");
+ bool replied = false;
+ voidResultFn(MyFnParams{4, 5}, [&](llvm::Error error) {
+ EXPECT_THAT_ERROR(std::move(error), Succeeded());
+ replied = true;
+ });
+ EXPECT_CALL(remote, Received(Request{1, "voidResult", MyFnParams{4, 5}}));
+ EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, std::nullopt}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(replied);
+}
+
+TEST_F(TransportBinderTest, OutBoundRequestsVoidParamsAndVoidResult) {
+ OutgoingRequest<void, void> voidParamAndResultFn =
+ binder->Bind<void, void>("voidParamAndResult");
+ bool replied = false;
+ voidParamAndResultFn([&](llvm::Error error) {
+ EXPECT_THAT_ERROR(std::move(error), Succeeded());
+ replied = true;
+ });
+ EXPECT_CALL(remote, Received(Request{1, "voidParamAndResult", std::nullopt}));
+ EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, std::nullopt}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(replied);
+}
+
+// In-bound binding request handler.
+TEST_F(TransportBinderTest, InBoundRequests) {
+ bool called = false;
+ binder->Bind<MyFnResult, MyFnParams>(
+ "add",
+ [&](const int captured_param,
+ const MyFnParams &params) -> Expected<MyFnResult> {
+ called = true;
+ return MyFnResult{params.a + params.b + captured_param};
+ },
+ 2);
+ EXPECT_THAT_ERROR(from_remote->Send(Request{1, "add", MyFnParams{3, 4}}),
+ Succeeded());
+
+ EXPECT_CALL(remote, Received(Response{1, 0, MyFnResult{9}}));
+ Run();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(TransportBinderTest, InBoundRequestsVoidParams) {
+ bool called = false;
+ binder->Bind<MyFnResult, void>(
+ "voidParam",
+ [&](const int captured_param) -> Expected<MyFnResult> {
+ called = true;
+ return MyFnResult{captured_param};
+ },
+ 2);
+ EXPECT_THAT_ERROR(from_remote->Send(Request{2, "voidParam", std::nullopt}),
+ Succeeded());
+ EXPECT_CALL(remote, Received(Response{2, 0, MyFnResult{2}}));
+ Run();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(TransportBinderTest, InBoundRequestsVoidResult) {
+ bool called = false;
+ binder->Bind<void, MyFnParams>(
+ "voidResult",
+ [&](const int captured_param, const MyFnParams &params) -> llvm::Error {
+ called = true;
+ EXPECT_EQ(captured_param, 2);
+ EXPECT_EQ(params.a, 3);
+ EXPECT_EQ(params.b, 4);
+ return llvm::Error::success();
+ },
+ 2);
+ EXPECT_THAT_ERROR(
+ from_remote->Send(Request{3, "voidResult", MyFnParams{3, 4}}),
+ Succeeded());
+ EXPECT_CALL(remote, Received(Response{3, 0, std::nullopt}));
+ Run();
+ EXPECT_TRUE(called);
+}
+TEST_F(TransportBinderTest, InBoundRequestsVoidParamsAndResult) {
+ bool called = false;
+ binder->Bind<void, void>(
+ "voidParamAndResult",
+ [&](const int captured_param) -> llvm::Error {
+ called = true;
+ EXPECT_EQ(captured_param, 2);
+ return llvm::Error::success();
+ },
+ 2);
+ EXPECT_THAT_ERROR(
+ from_remote->Send(Request{4, "voidParamAndResult", std::nullopt}),
+ Succeeded());
+ EXPECT_CALL(remote, Received(Response{4, 0, std::nullopt}));
+ Run();
+ EXPECT_TRUE(called);
+}
+
+// Out-bound binding event handler.
+TEST_F(TransportBinderTest, OutBoundEvents) {
+ OutgoingEvent<MyFnParams> emitEvent = binder->Bind<MyFnParams>("evt");
+ emitEvent(MyFnParams{1, 2});
+ EXPECT_CALL(remote, Received(test_protocol::Event{"evt", MyFnParams{1, 2}}));
+ Run();
+}
+
+TEST_F(TransportBinderTest, OutBoundEventsVoidParams) {
+ OutgoingEvent<void> emitEvent = binder->Bind<void>("evt");
+ emitEvent();
+ EXPECT_CALL(remote, Received(test_protocol::Event{"evt", std::nullopt}));
+ Run();
+}
+
+// In-bound binding event handler.
+TEST_F(TransportBinderTest, InBoundEvents) {
+ bool called = false;
+ binder->Bind<MyFnParams>(
+ "evt",
+ [&](const int captured_arg, const MyFnParams &params) {
+ EXPECT_EQ(captured_arg, 42);
+ EXPECT_EQ(params.a, 3);
+ EXPECT_EQ(params.b, 4);
+ called = true;
+ },
+ 42);
+ EXPECT_THAT_ERROR(
+ from_remote->Send(test_protocol::Event{"evt", MyFnParams{3, 4}}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(TransportBinderTest, InBoundEventsVoidParams) {
+ bool called = false;
+ binder->Bind<void>(
+ "evt",
+ [&](const int captured_arg) {
+ EXPECT_EQ(captured_arg, 42);
+ called = true;
+ },
+ 42);
+ EXPECT_THAT_ERROR(
+ from_remote->Send(test_protocol::Event{"evt", std::nullopt}),
+ Succeeded());
+ Run();
+ EXPECT_TRUE(called);
+}
+
#endif
diff --git a/lldb/unittests/Host/MainLoopTest.cpp b/lldb/unittests/Host/MainLoopTest.cpp
index 0bc291c26b9c..ae16d0210181 100644
--- a/lldb/unittests/Host/MainLoopTest.cpp
+++ b/lldb/unittests/Host/MainLoopTest.cpp
@@ -424,9 +424,9 @@ TEST_F(MainLoopTest, ManyPendingCallbacks) {
TEST_F(MainLoopTest, CallbackWithTimeout) {
MainLoop loop;
+ auto start = std::chrono::steady_clock::now();
loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
std::chrono::seconds(2));
- auto start = std::chrono::steady_clock::now();
ASSERT_THAT_ERROR(loop.Run().takeError(), llvm::Succeeded());
EXPECT_GE(std::chrono::steady_clock::now() - start, std::chrono::seconds(2));
}
diff --git a/lldb/unittests/Host/posix/HostTest.cpp b/lldb/unittests/Host/posix/HostTest.cpp
index dc75b288ba76..7135f266a350 100644
--- a/lldb/unittests/Host/posix/HostTest.cpp
+++ b/lldb/unittests/Host/posix/HostTest.cpp
@@ -15,10 +15,6 @@
#include <cerrno>
#include <sys/resource.h>
-#ifdef __linux__
-#include <linux/version.h>
-#endif // __linux__
-
using namespace lldb_private;
namespace {
@@ -120,12 +116,13 @@ TEST_F(HostTest, GetProcessInfoSetsPriority) {
ASSERT_TRUE(Info.IsZombie().has_value());
ASSERT_FALSE(Info.IsZombie().value());
- // CoreDumping was added in kernel version 4.15.
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
- ASSERT_TRUE(Info.IsCoreDumping().has_value());
- ASSERT_FALSE(Info.IsCoreDumping().value());
-#else
- ASSERT_FALSE(Info.IsCoreDumping().has_value());
-#endif
+ const llvm::VersionTuple host_version = HostInfo::GetOSVersion();
+ ASSERT_FALSE(host_version.empty());
+ if (host_version >= llvm::VersionTuple(4, 15, 0)) {
+ ASSERT_TRUE(Info.IsCoreDumping().has_value());
+ ASSERT_FALSE(Info.IsCoreDumping().value());
+ } else {
+ ASSERT_FALSE(Info.IsCoreDumping().has_value());
+ }
}
#endif
diff --git a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
index f94022998588..012eae02d585 100644
--- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -639,3 +639,25 @@ TEST_F(GDBRemoteCommunicationClientTest, CalculateMD5) {
EXPECT_EQ(expected_high, result->high());
}
#endif
+
+TEST_F(GDBRemoteCommunicationClientTest, MultiMemReadSupported) {
+ std::future<bool> async_result = std::async(std::launch::async, [&] {
+ StringExtractorGDBRemote qSupported_packet_request;
+ server.GetPacket(qSupported_packet_request);
+ server.SendPacket("MultiMemRead+;");
+ return true;
+ });
+ ASSERT_TRUE(client.GetMultiMemReadSupported());
+ async_result.wait();
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, MultiMemReadNotSupported) {
+ std::future<bool> async_result = std::async(std::launch::async, [&] {
+ StringExtractorGDBRemote qSupported_packet_request;
+ server.GetPacket(qSupported_packet_request);
+ server.SendPacket(";");
+ return true;
+ });
+ ASSERT_FALSE(client.GetMultiMemReadSupported());
+ async_result.wait();
+}
diff --git a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
index f3ca4cfc0178..45464db958e0 100644
--- a/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPServerTest.cpp
@@ -6,9 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#include "ProtocolMCPTestUtilities.h"
+#include "ProtocolMCPTestUtilities.h" // IWYU pragma: keep
#include "TestingSupport/Host/JSONTransportTestUtilities.h"
-#include "TestingSupport/Host/PipeTestUtilities.h"
#include "TestingSupport/SubsystemRAII.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
@@ -28,20 +27,25 @@
#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include <chrono>
-#include <condition_variable>
+#include <future>
+#include <memory>
+#include <optional>
+#include <system_error>
using namespace llvm;
using namespace lldb;
using namespace lldb_private;
+using namespace lldb_private::transport;
using namespace lldb_protocol::mcp;
+// Flakey, see https://github.com/llvm/llvm-project/issues/152677.
+#ifndef _WIN32
+
namespace {
-class TestServer : public Server {
-public:
- using Server::Server;
-};
+template <typename T> Response make_response(T &&result, Id id = 1) {
+ return Response{id, std::forward<T>(result)};
+}
/// Test tool that returns it argument as text.
class TestTool : public Tool {
@@ -101,7 +105,9 @@ public:
using Tool::Tool;
llvm::Expected<CallToolResult> Call(const ToolArguments &args) override {
- return llvm::createStringError("error");
+ return llvm::createStringError(
+ std::error_code(eErrorCodeInternalError, std::generic_category()),
+ "error");
}
};
@@ -118,195 +124,209 @@ public:
}
};
-class ProtocolServerMCPTest : public PipePairTest {
+class TestServer : public Server {
+public:
+ using Server::Bind;
+ using Server::Server;
+};
+
+using Transport = TestTransport<lldb_protocol::mcp::ProtocolDescriptor>;
+
+class ProtocolServerMCPTest : public testing::Test {
public:
SubsystemRAII<FileSystem, HostInfo, Socket> subsystems;
MainLoop loop;
+ lldb_private::MainLoop::ReadHandleUP handles[2];
- std::unique_ptr<lldb_protocol::mcp::Transport> from_client;
- std::unique_ptr<lldb_protocol::mcp::Transport> to_client;
- MainLoopBase::ReadHandleUP handles[2];
-
+ std::unique_ptr<Transport> to_server;
+ MCPBinderUP binder;
std::unique_ptr<TestServer> server_up;
- 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 from_client->Write(*value);
- }
+ std::unique_ptr<Transport> to_client;
+ MockMessageHandler<lldb_protocol::mcp::ProtocolDescriptor> client;
- llvm::Error Write(json::Value value) { return from_client->Write(value); }
+ std::vector<std::string> logged_messages;
- /// Run the transport MainLoop and return any messages received.
- llvm::Error Run() {
- loop.AddCallback([](MainLoopBase &loop) { loop.RequestTermination(); },
- std::chrono::milliseconds(10));
- return loop.Run().takeError();
+ /// Runs the MainLoop a single time, executing any pending callbacks.
+ void Run() {
+ loop.AddPendingCallback(
+ [](MainLoopBase &loop) { loop.RequestTermination(); });
+ EXPECT_THAT_ERROR(loop.Run().takeError(), Succeeded());
}
void SetUp() override {
- PipePairTest::SetUp();
-
- from_client = std::make_unique<lldb_protocol::mcp::Transport>(
- std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
- File::eOpenOptionReadOnly,
- NativeFile::Unowned),
- std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
- File::eOpenOptionWriteOnly,
- NativeFile::Unowned),
- [](StringRef message) {
- // Uncomment for debugging
- // llvm::errs() << "from_client: " << message << '\n';
- });
- to_client = std::make_unique<lldb_protocol::mcp::Transport>(
- std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
- File::eOpenOptionReadOnly,
- NativeFile::Unowned),
- std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
- File::eOpenOptionWriteOnly,
- NativeFile::Unowned),
- [](StringRef message) {
- // Uncomment for debugging
- // llvm::errs() << "to_client: " << message << '\n';
- });
-
- server_up = std::make_unique<TestServer>("lldb-mcp", "0.1.0", *to_client,
- [](StringRef message) {
- // Uncomment for debugging
- // llvm::errs() << "server: " <<
- // message << '\n';
- });
-
- auto maybe_from_client_handle =
- from_client->RegisterMessageHandler(loop, message_handler);
- EXPECT_THAT_EXPECTED(maybe_from_client_handle, Succeeded());
- handles[0] = std::move(*maybe_from_client_handle);
-
- auto maybe_to_client_handle =
- to_client->RegisterMessageHandler(loop, *server_up);
- EXPECT_THAT_EXPECTED(maybe_to_client_handle, Succeeded());
- handles[1] = std::move(*maybe_to_client_handle);
+ std::tie(to_client, to_server) = Transport::createPair();
+
+ server_up = std::make_unique<TestServer>(
+ "lldb-mcp", "0.1.0",
+ [this](StringRef msg) { logged_messages.push_back(msg.str()); });
+ binder = server_up->Bind(*to_client);
+ auto server_handle = to_server->RegisterMessageHandler(loop, *binder);
+ EXPECT_THAT_EXPECTED(server_handle, Succeeded());
+ binder->OnError([](llvm::Error error) {
+ llvm::errs() << formatv("Server transport error: {0}", error);
+ });
+ handles[0] = std::move(*server_handle);
+
+ auto client_handle = to_client->RegisterMessageHandler(loop, client);
+ EXPECT_THAT_EXPECTED(client_handle, Succeeded());
+ handles[1] = std::move(*client_handle);
+ }
+
+ template <typename Result, typename Params>
+ Expected<json::Value> Call(StringRef method, const Params &params) {
+ std::promise<Response> promised_result;
+ Request req =
+ lldb_protocol::mcp::Request{/*id=*/1, method.str(), toJSON(params)};
+ EXPECT_THAT_ERROR(to_server->Send(req), Succeeded());
+ EXPECT_CALL(client, Received(testing::An<const Response &>()))
+ .WillOnce(
+ [&](const Response &resp) { promised_result.set_value(resp); });
+ Run();
+ Response resp = promised_result.get_future().get();
+ return toJSON(resp);
+ }
+
+ template <typename Result>
+ Expected<json::Value>
+ Capture(llvm::unique_function<void(Reply<Result>)> &fn) {
+ std::promise<llvm::Expected<Result>> promised_result;
+ fn([&promised_result](llvm::Expected<Result> result) {
+ promised_result.set_value(std::move(result));
+ });
+ Run();
+ llvm::Expected<Result> result = promised_result.get_future().get();
+ if (!result)
+ return result.takeError();
+ return toJSON(*result);
+ }
+
+ template <typename Result, typename Params>
+ Expected<json::Value>
+ Capture(llvm::unique_function<void(const Params &, Reply<Result>)> &fn,
+ const Params &params) {
+ std::promise<llvm::Expected<Result>> promised_result;
+ fn(params, [&promised_result](llvm::Expected<Result> result) {
+ promised_result.set_value(std::move(result));
+ });
+ Run();
+ llvm::Expected<Result> result = promised_result.get_future().get();
+ if (!result)
+ return result.takeError();
+ return toJSON(*result);
}
};
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)};
+inline testing::internal::EqMatcher<llvm::json::Value> HasJSON(T x) {
+ return testing::internal::EqMatcher<llvm::json::Value>(toJSON(x));
}
} // 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());
+ EXPECT_THAT_EXPECTED(
+ (Call<InitializeResult, InitializeParams>(
+ "initialize",
+ InitializeParams{/*protocolVersion=*/"2024-11-05",
+ /*capabilities=*/{},
+ /*clientInfo=*/{"lldb-unit", "0.1.0"}})),
+ HasValue(make_response(
+ InitializeResult{/*protocolVersion=*/"2024-11-05",
+ /*capabilities=*/
+ {
+ /*supportsToolsList=*/true,
+ /*supportsResourcesList=*/true,
+ },
+ /*serverInfo=*/{"lldb-mcp", "0.1.0"}})));
}
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());
+ EXPECT_THAT_EXPECTED(Call<ListToolsResult>("tools/list", Void{}),
+ HasValue(make_response(ListToolsResult{{test_tool}})));
}
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());
+ EXPECT_THAT_EXPECTED(Call<ListResourcesResult>("resources/list", Void{}),
+ HasValue(make_response(ListResourcesResult{{
+ {
+ /*uri=*/"lldb://foo/bar",
+ /*name=*/"name",
+ /*description=*/"description",
+ /*mimeType=*/"application/json",
+ },
+ }})));
}
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());
+ EXPECT_THAT_EXPECTED(
+ (Call<CallToolResult, CallToolParams>("tools/call",
+ CallToolParams{
+ /*name=*/"test",
+ /*arguments=*/
+ json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ },
+ })),
+ HasValue(make_response(CallToolResult{{{/*text=*/"foo"}}})));
}
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());
+ EXPECT_THAT_EXPECTED((Call<CallToolResult, CallToolParams>(
+ "tools/call", CallToolParams{
+ /*name=*/"error",
+ /*arguments=*/
+ json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ },
+ })),
+ HasValue(make_response(lldb_protocol::mcp::Error{
+ eErrorCodeInternalError, "error"})));
}
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());
+ EXPECT_THAT_EXPECTED((Call<CallToolResult, CallToolParams>(
+ "tools/call", CallToolParams{
+ /*name=*/"fail",
+ /*arguments=*/
+ json::Object{
+ {"arguments", "foo"},
+ {"debugger_id", 0},
+ },
+ })),
+ HasValue(make_response(CallToolResult{
+ {{/*text=*/"failed"}},
+ /*isError=*/true,
+ })));
}
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);
+ EXPECT_THAT_ERROR(to_server->Send(lldb_protocol::mcp::Notification{
+ "notifications/initialized",
+ std::nullopt,
+ }),
+ Succeeded());
+ Run();
+ EXPECT_THAT(logged_messages,
+ testing::Contains("MCP initialization complete"));
}
+
+#endif
diff --git a/lldb/unittests/Protocol/ProtocolMCPTest.cpp b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
index 396e361e873f..5f7391e43fe3 100644
--- a/lldb/unittests/Protocol/ProtocolMCPTest.cpp
+++ b/lldb/unittests/Protocol/ProtocolMCPTest.cpp
@@ -16,6 +16,9 @@ using namespace lldb;
using namespace lldb_private;
using namespace lldb_protocol::mcp;
+// Flakey, see https://github.com/llvm/llvm-project/issues/152677.
+#ifndef _WIN32
+
TEST(ProtocolMCPTest, Request) {
Request request;
request.id = 1;
@@ -292,3 +295,5 @@ TEST(ProtocolMCPTest, ReadResourceResultEmpty) {
EXPECT_TRUE(deserialized_result->contents.empty());
}
+
+#endif
diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
index 068860ebc20f..6f5d9fd97ee2 100644
--- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
+++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
@@ -105,6 +105,11 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpoint(
return nullptr;
}
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBBreakpointLocation(
+ PyObject *data) {
+ return nullptr;
+}
+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBAttachInfo(
PyObject *data) {
return nullptr;
@@ -130,6 +135,11 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject *data) {
return nullptr;
}
+void *
+lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrame(PyObject *data) {
+ return nullptr;
+}
+
void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBSymbolContext(
PyObject *data) {
return nullptr;
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index f673cceae00d..1981e912fa4f 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -162,6 +162,9 @@ TEST_F(TestTypeSystemClang, TestGetBasicTypeFromName) {
EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128_t"));
EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128),
GetBasicQualType("__uint128_t"));
+ EXPECT_EQ(GetBasicQualType(eBasicTypeInt128), GetBasicQualType("__int128"));
+ EXPECT_EQ(GetBasicQualType(eBasicTypeUnsignedInt128),
+ GetBasicQualType("unsigned __int128"));
EXPECT_EQ(GetBasicQualType(eBasicTypeVoid), GetBasicQualType("void"));
EXPECT_EQ(GetBasicQualType(eBasicTypeBool), GetBasicQualType("bool"));
EXPECT_EQ(GetBasicQualType(eBasicTypeFloat), GetBasicQualType("float"));
diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt
index 3169339ec699..83eec3b1117b 100644
--- a/lldb/unittests/Target/CMakeLists.txt
+++ b/lldb/unittests/Target/CMakeLists.txt
@@ -2,6 +2,7 @@ add_lldb_unittest(TargetTests
ABITest.cpp
DynamicRegisterInfoTest.cpp
ExecutionContextTest.cpp
+ LanguageTest.cpp
LocateModuleCallbackTest.cpp
MemoryRegionInfoTest.cpp
MemoryTest.cpp
diff --git a/lldb/unittests/Target/LanguageTest.cpp b/lldb/unittests/Target/LanguageTest.cpp
new file mode 100644
index 000000000000..720863bda68e
--- /dev/null
+++ b/lldb/unittests/Target/LanguageTest.cpp
@@ -0,0 +1,82 @@
+//===-- LanguageTest.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 "lldb/Target/Language.h"
+#include "lldb/lldb-enumerations.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+namespace {
+class LanguageTest : public ::testing::Test {};
+} // namespace
+
+TEST_F(LanguageTest, SourceLanguage_GetDescription) {
+ for (uint32_t i = 1; i < lldb::eNumLanguageTypes; ++i) {
+ // 0x29 is unassigned
+ if (i == 0x29)
+ continue;
+
+ auto lang_type = static_cast<lldb::LanguageType>(i);
+ SourceLanguage lang(lang_type);
+
+ // eLanguageTypeHIP is not implemented as a DW_LNAME because of a conflict.
+ if (lang_type == lldb::eLanguageTypeHIP)
+ EXPECT_FALSE(lang);
+ else
+ EXPECT_TRUE(lang);
+ }
+
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus).GetDescription(),
+ "ISO C++");
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_17).GetDescription(),
+ "C++17");
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_20).GetDescription(),
+ "C++20");
+
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC).GetDescription(), "C (K&R and ISO)");
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC89).GetDescription(), "C89");
+
+ EXPECT_EQ(SourceLanguage(eLanguageTypeObjC).GetDescription(), "Objective C");
+ EXPECT_EQ(SourceLanguage(eLanguageTypeMipsAssembler).GetDescription(),
+ "Assembly");
+
+ auto next_vendor_language =
+ static_cast<lldb::LanguageType>(eLanguageTypeMipsAssembler + 1);
+ if (next_vendor_language < eNumLanguageTypes)
+ EXPECT_NE(SourceLanguage(next_vendor_language).GetDescription(), "Unknown");
+
+ EXPECT_EQ(SourceLanguage(eLanguageTypeUnknown).GetDescription(), "Unknown");
+}
+
+TEST_F(LanguageTest, SourceLanguage_AsLanguageType) {
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus).AsLanguageType(),
+ eLanguageTypeC_plus_plus);
+ EXPECT_EQ(SourceLanguage(eLanguageTypeC_plus_plus_03).AsLanguageType(),
+ eLanguageTypeC_plus_plus_03);
+
+ // Vendor-specific language code.
+ EXPECT_EQ(SourceLanguage(eLanguageTypeMipsAssembler).AsLanguageType(),
+ eLanguageTypeAssembly);
+ EXPECT_EQ(SourceLanguage(eLanguageTypeUnknown).AsLanguageType(),
+ eLanguageTypeUnknown);
+}
+
+TEST_F(LanguageTest, SourceLanguage_LastStandardLanguage) {
+ // eLanguageTypeLastStandardLanguage should be treated as a standard DWARF
+ // language.
+ SourceLanguage lang(eLanguageTypeLastStandardLanguage);
+ EXPECT_TRUE(lang);
+
+ // It should have a valid description (not "Unknown").
+ EXPECT_NE(lang.GetDescription(), "Unknown");
+
+ // It should convert to the correct language type.
+ EXPECT_EQ(lang.AsLanguageType(), eLanguageTypeLastStandardLanguage);
+}
diff --git a/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h b/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h
index 5a9eb8e59f2b..bacf8ca36aa0 100644
--- a/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h
+++ b/lldb/unittests/TestingSupport/Host/JSONTransportTestUtilities.h
@@ -6,19 +6,105 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H
-#define LLDB_UNITTESTS_TESTINGSUPPORT_HOST_NATIVEPROCESSTESTUTILS_H
+#ifndef LLDB_UNITTESTS_TESTINGSUPPORT_HOST_JSONTRANSPORTTESTUTILITIES_H
+#define LLDB_UNITTESTS_TESTINGSUPPORT_HOST_JSONTRANSPORTTESTUTILITIES_H
+#include "lldb/Host/FileSystem.h"
#include "lldb/Host/JSONTransport.h"
+#include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Testing/Support/Error.h"
#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <cstddef>
+#include <memory>
+#include <utility>
-template <typename Req, typename Resp, typename Evt>
+template <typename Proto>
+class TestTransport final
+ : public lldb_private::transport::JSONTransport<Proto> {
+public:
+ using MessageHandler =
+ typename lldb_private::transport::JSONTransport<Proto>::MessageHandler;
+
+ static std::pair<std::unique_ptr<TestTransport<Proto>>,
+ std::unique_ptr<TestTransport<Proto>>>
+ createPair() {
+ std::unique_ptr<TestTransport<Proto>> transports[2] = {
+ std::make_unique<TestTransport<Proto>>(),
+ std::make_unique<TestTransport<Proto>>()};
+ return std::make_pair(std::move(transports[0]), std::move(transports[1]));
+ }
+
+ explicit TestTransport() {
+ llvm::Expected<lldb::FileUP> dummy_file =
+ lldb_private::FileSystem::Instance().Open(
+ lldb_private::FileSpec(lldb_private::FileSystem::DEV_NULL),
+ lldb_private::File::eOpenOptionReadWrite);
+ EXPECT_THAT_EXPECTED(dummy_file, llvm::Succeeded());
+ m_dummy_file = std::move(*dummy_file);
+ }
+
+ llvm::Error Send(const typename Proto::Evt &evt) override {
+ EXPECT_TRUE(m_loop && m_handler)
+ << "Send called before RegisterMessageHandler";
+ m_loop->AddPendingCallback([this, evt](lldb_private::MainLoopBase &) {
+ m_handler->Received(evt);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Error Send(const typename Proto::Req &req) override {
+ EXPECT_TRUE(m_loop && m_handler)
+ << "Send called before RegisterMessageHandler";
+ m_loop->AddPendingCallback([this, req](lldb_private::MainLoopBase &) {
+ m_handler->Received(req);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Error Send(const typename Proto::Resp &resp) override {
+ EXPECT_TRUE(m_loop && m_handler)
+ << "Send called before RegisterMessageHandler";
+ m_loop->AddPendingCallback([this, resp](lldb_private::MainLoopBase &) {
+ m_handler->Received(resp);
+ });
+ return llvm::Error::success();
+ }
+
+ llvm::Expected<lldb_private::MainLoop::ReadHandleUP>
+ RegisterMessageHandler(lldb_private::MainLoop &loop,
+ MessageHandler &handler) override {
+ if (!m_loop)
+ m_loop = &loop;
+ if (!m_handler)
+ m_handler = &handler;
+ lldb_private::Status status;
+ auto handle = loop.RegisterReadObject(
+ m_dummy_file, [](lldb_private::MainLoopBase &) {}, status);
+ if (status.Fail())
+ return status.takeError();
+ return handle;
+ }
+
+protected:
+ void Log(llvm::StringRef message) override {};
+
+private:
+ lldb_private::MainLoop *m_loop = nullptr;
+ MessageHandler *m_handler = nullptr;
+ // Dummy file for registering with the MainLoop.
+ lldb::FileSP m_dummy_file = nullptr;
+};
+
+template <typename Proto>
class MockMessageHandler final
- : public lldb_private::Transport<Req, Resp, Evt>::MessageHandler {
+ : public lldb_private::transport::JSONTransport<Proto>::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, Received, (const typename Proto::Req &), (override));
+ MOCK_METHOD(void, Received, (const typename Proto::Resp &), (override));
+ MOCK_METHOD(void, Received, (const typename Proto::Evt &), (override));
MOCK_METHOD(void, OnError, (llvm::Error), (override));
MOCK_METHOD(void, OnClosed, (), (override));
};
diff --git a/lldb/unittests/Utility/XcodeSDKTest.cpp b/lldb/unittests/Utility/XcodeSDKTest.cpp
index de9f91a04d53..a8a597bdeb74 100644
--- a/lldb/unittests/Utility/XcodeSDKTest.cpp
+++ b/lldb/unittests/Utility/XcodeSDKTest.cpp
@@ -27,6 +27,7 @@ TEST(XcodeSDKTest, ParseTest) {
EXPECT_EQ(XcodeSDK("AppleTVOS.sdk").GetType(), XcodeSDK::AppleTVOS);
EXPECT_EQ(XcodeSDK("WatchSimulator.sdk").GetType(), XcodeSDK::WatchSimulator);
EXPECT_EQ(XcodeSDK("WatchOS.sdk").GetType(), XcodeSDK::watchOS);
+ EXPECT_EQ(XcodeSDK("BridgeOS.sdk").GetType(), XcodeSDK::BridgeOS);
EXPECT_EQ(XcodeSDK("XRSimulator.sdk").GetType(), XcodeSDK::XRSimulator);
EXPECT_EQ(XcodeSDK("XROS.sdk").GetType(), XcodeSDK::XROS);
EXPECT_EQ(XcodeSDK("Linux.sdk").GetType(), XcodeSDK::Linux);
diff --git a/lldb/utils/lui/lldbutil.py b/lldb/utils/lui/lldbutil.py
index 140317af3670..589acaeea320 100644
--- a/lldb/utils/lui/lldbutil.py
+++ b/lldb/utils/lui/lldbutil.py
@@ -951,7 +951,7 @@ def get_GPRs(frame):
from lldbutil import get_GPRs
regs = get_GPRs(frame)
for reg in regs:
- print "%s => %s" % (reg.GetName(), reg.GetValue())
+ print("%s => %s" % (reg.GetName(), reg.GetValue()))
...
"""
return get_registers(frame, "general purpose")
@@ -965,7 +965,7 @@ def get_FPRs(frame):
from lldbutil import get_FPRs
regs = get_FPRs(frame)
for reg in regs:
- print "%s => %s" % (reg.GetName(), reg.GetValue())
+ print("%s => %s" % (reg.GetName(), reg.GetValue()))
...
"""
return get_registers(frame, "floating point")
diff --git a/lldb/utils/lui/lui.py b/lldb/utils/lui/lui.py
index 42ea3c678730..27ed8f382af8 100755
--- a/lldb/utils/lui/lui.py
+++ b/lldb/utils/lui/lui.py
@@ -17,11 +17,7 @@ from optparse import OptionParser
import os
import signal
import sys
-
-try:
- import queue
-except ImportError:
- import Queue as queue
+import queue
import debuggerdriver
import cui
diff --git a/lldb/utils/lui/sandbox.py b/lldb/utils/lui/sandbox.py
index 3bcf3d5915d9..d05c2682c979 100755
--- a/lldb/utils/lui/sandbox.py
+++ b/lldb/utils/lui/sandbox.py
@@ -14,10 +14,7 @@ import os
import signal
import sys
-try:
- import queue
-except ImportError:
- import Queue as queue
+import queue
import cui